收集以跳过空值的映射

Eld*_*rov 6 kotlin

如何从列表中收集地图,其中排除/跳过空值?

此代码不会跳过空值:

val map = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
    .associateBy({ it.first }, { it.second })
println(map)
Run Code Online (Sandbox Code Playgroud)

解决方案.但收集到可变的地图:

val map2 = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
    .mapNotNull {
        if (it.second != null) it else null
    }.toMap()    
println(map2)
Run Code Online (Sandbox Code Playgroud)

那么有更方便的方法吗?我也希望得到Map<String, Int>类型,而不是Map<String, Int?>

小智 11

从 Kotlin 1.6 开始,还有一个稳定的buildMap函数,可用于编写高性能且不牺牲可读性的自定义辅助函数:

fun <T, K : Any, V : Any> Iterable<T>.associateByNotNull(
    keySelector: (T) -> K?,
    valueTransform: (T) -> V?,
): Map<K, V> = buildMap {
    for (item in this@associateByNotNull) {
        val key = keySelector(item) ?: continue
        val value = valueTransform(item) ?: continue
        this[key] = value
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,将其编写为“低级”for 循环可以消除创建中间集合的需要。


pwo*_*laq 9

你想过滤掉null值,那么你应该使用filter方法:

val map = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
    .filter { it.second != null }
    .toMap()
println(map)
Run Code Online (Sandbox Code Playgroud)

  • 另外,您的代码返回可为 null 类型的 Map,即不是“Map&lt;String, Int&gt;”,而是“Map&lt;String, Int?&gt;” (7认同)

Moi*_*ira 7

实际上,对pwolaq的回答略有改变,保证第二项不可为空:

val map = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
    .mapNotNull { p -> p.second?.let { Pair(p.first, it) } }
    .toMap()
println(map)
Run Code Online (Sandbox Code Playgroud)

这将给你一个Map<String, Int>,因为mapNotNull忽略了映射到的任何东西null,并且使用let安全调用操作符,?.如果它的receiver(p.second)是,则返回null null.

这基本上就是你在问题中所说的,缩短了let.

你是什​​么意思"收集到可变地图"?