【问题标题】:Transform a list, filtering out the items that cause an exception转换列表,过滤掉导致异常的项目
【发布时间】:2018-05-09 09:24:45
【问题描述】:

如何转换这个String数组:

"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 */
    }
}

【问题讨论】:

  • 你只需要将filter 取出“n/a”值,然后使用map,有什么问题?
  • 您是要将错误日期保留为空,还是将其删除?
  • @jrtapsell 我正在尝试删除错误的日期。考虑到给定示例,3 个元素的初始字符串数组应转换为 2 个元素的日期数组。 “n/a”应该被忽略,我有兴趣使用 Kotlin 高阶函数来完成这项任务。
  • @pwolaq 谢谢,这是选项。

标签: kotlin reduce higher-order-functions


【解决方案1】:

使用 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)

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

使用扩展函数

您还可以将 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)

【讨论】:

  • tryOrRemove 不是一个好名字,因为它并不表示它正在转换所有项目,而不仅仅是删除那些未能通过 try-catch 测试的项目。
【解决方案2】:

您正在寻找一种可以为每个输入元素输出零个或一个元素的转换。这是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 会是更好的选择
  • @pwolaq 我用过那个here,但是对于小列表,临时列表的开销应该不会太差,尽管如果可能的话应该避免
  • 我同意这样更好,最好提取SimpleDateFormat 实例。如果标准库中有tryOrNull就更好了。
猜你喜欢
  • 2021-07-12
  • 2021-02-23
  • 2013-07-27
  • 2022-11-02
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-05-11
  • 1970-01-01
相关资源
最近更新 更多