转换列表,过滤掉导致异常的项目

Transform a list, filtering out the items that cause an exception

如何转换这个字符串数组:

"2018-05-08T23:22:49Z" "n/a" "2018-05-07T16:37:00Z"

到使用高阶函数的日期数组,例如 mapflatMapreduce?

我知道使用 forEach 可以做到这一点,但我有兴趣涉及 Kotlin 高阶函数:

val stringArray
        = mutableListOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")

val dateArray = mutableListOf<Date>()

stringArray.forEach {
    try {
        val date = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
                .parse(it)
        dateArray.add(date)
    } catch (e: ParseException) {
        //* Just prevents app from crash */
    }
}

您正在寻找一种可以为每个输入元素输出零个或一个元素的转换。这是flatMap。平面映射函数的结果必须是 Iterable,所以:

val dateArray = stringArray.flatMap {
    try {
        listOf(SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US).parse(it))
    } catch (e: ParseException) {
        emptyList<Date>()
    }
}

根据@pwolaq的输入添加以下内容:

强烈建议提取 SimpleDateFormat 实例,因为它具有重量级初始化。此外,mapNotNull 的解决方案比 flatMap 更干净,我没有意识到这一点。如果您添加一个我认为 Kotlin 标准库中缺少的函数,这将变得特别方便:

inline fun <T> runOrNull(block: () -> T) = try {
    block()
} catch (t: Throwable) {
    null
}

在你的工具箱中有了这个,你可以说:

val formatter = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dateArray: List<Date> = stringArray.mapNotNull { 
    runOrNull { formatter.parse(it) } 
}

使用 mapNotNull

val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)
val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
    .mapNotNull {
        try {
            format.parse(it)
        } catch (e: ParseException) {
            null
        }
    }
println(dates)

这避免了为列表中的每个项目创建一个列表,它将错误日期映射到空值,并且 mapNotNull 从列表中删除空值。

使用扩展函数

您还可以将 tryOrRemove 提取到扩展函数中,使代码如下所示:

val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)

fun <T, U: Any> Iterable<T>.tryOrRemove(block:(T)->U): List<U> {
    return mapNotNull {
        try {
            block(it)
        } catch (ex: Throwable) {
            null
        }
    }
}

val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
    .tryOrRemove(format::parse)

println(dates)

使用过滤器

我已经根据唯一的错误日期 n/a 编写了它,这简化了它。

val format = SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss'Z'", Locale.US)

val dates = listOf("2018-05-08T23:22:49Z", "n/a", "2018-05-07T16:37:00Z")
    .filter { it != "n/a" }
    .map(format::parse)

println(dates)