在Kotlin中是否可以将过滤器和映射合并为单个操作?

时间:2019-05-03 01:44:13

标签: kotlin

以下代码将查找"=",然后将其拆分。如果没有"=",请先将其过滤掉

myPairStr.asSequence()
        .filter { it.contains("=") }
        .map { it.split("=") }

但是看到我们两者都有

        .filter { it.contains("=") }
        .map { it.split("=") }

想知道是否有单个操作可以将操作组合在一起而不是单独进行?

2 个答案:

答案 0 :(得分:0)

您可以使用mapNotNull代替map

myPairStr.asSequence().mapNotNull { it.split("=").takeIf { it.size >= 2 } }

如果takeIf方法返回的null的大小为 1 ,即{{1} }不在字符串中。并且list将仅接受split个值并将它们放在列表中(最终返回)。 在您的情况下,此解决方案将起作用。在其他情况下,(合并=mapNotNull的实现可能会有所不同。

答案 1 :(得分:0)

我明白了你的意思,split也在做indexOf检查,以获取适当的部分。

我不知道有任何这样的功能可以在一个操作中同时支持这两种操作,即使该功能基本上与我们已经实现private fun split的功能相似。

因此,如果您真的想一步一步实现(并且更频繁地需要该功能),则可能要实现自己的splitOrNull功能,基本上是复制当前的(私有)split实现并主要修改其中的3个部分(返回类型List<String>?,如果indexOf传递了-1的条件,我们只返回null;还有一些默认值可以使其轻松实现可用(ignoreCase=falselimit=0);用// added// changed标记更改):

fun CharSequence.splitOrNull(delimiter: String, ignoreCase: Boolean = false, limit: Int = 0): List<String>? { // changed
    require(limit >= 0, { "Limit must be non-negative, but was $limit." })

    var currentOffset = 0
    var nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
    if (nextIndex == -1 || limit == 1) {
        if (currentOffset == 0 && nextIndex == -1) // added
            return null                            // added
        return listOf(this.toString())
    }

    val isLimited = limit > 0
    val result = ArrayList<String>(if (isLimited) limit.coerceAtMost(10) else 10)
    do {
        result.add(substring(currentOffset, nextIndex))
        currentOffset = nextIndex + delimiter.length
        // Do not search for next occurrence if we're reaching limit
        if (isLimited && result.size == limit - 1) break
        nextIndex = indexOf(delimiter, currentOffset, ignoreCase)
    } while (nextIndex != -1)

    result.add(substring(currentOffset, length))
    return result
}

有了这样的功能,您就可以将contains / indexOfsplit汇总为一个调用:

myPairStr.asSequence()
         .mapNotNull {
           it.splitOrNull("=") // or: it.splitOrNull("=", limit = 2)
         }

否则,您当前的方法已经足够好。它的一种变化只是在分割后检查分割的大小(基本上消除了写contains('=')的需要,而只是检查期望的大小,例如:

myPairStr.asSequence()
         .map { it.split('=') } 
         .filter { it.size > 1 }

如果要拆分$key=$value格式,其中value实际上可以包含其他=,则可能要改用以下内容:

myPairStr.asSequence()
         .map { it.split('=', limit = 2) } 
         .filter { it.size > 1 }
//       .associate { (key, value) -> key to value }