Reputation: 5227
How to collect to Map from List where null values are excluded/skipped?
This code doesn't skip null values:
val map = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
.associateBy({ it.first }, { it.second })
println(map)
Workaround solution. But collects into mutable map:
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)
So is there more convenient way to do this? Also I want to get Map<String, Int>
type, not Map<String, Int?>
Upvotes: 37
Views: 35764
Reputation: 363
Since Kotlin 1.6, there is also a stable buildMap function that can be used to write custom helper functions that are performant without sacrificing readability:
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
}
}
Note that writing this as a "low-level" for loop eliminates the need for the creation of intermediate collections.
Upvotes: 14
Reputation: 14907
Actually, a slight change to pwolaq's answer guarantees that the second item is non-nullable:
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)
This will give you a Map<String, Int>
, since mapNotNull
ignores anything that maps to null
, and using let
with the safe call operator ?.
returns null if its receiver (p.second
) is null
.
This is basically what you stated in your question, made shorter with let
.
Upvotes: 45
Reputation: 49
A more readable solution might be using the associateBy function with the double bang expression (!!):
val map: Map<String, Int> = listOf(
Pair("a", 1),
Pair("b", null),
Pair("c", 3),
Pair("d", null)
)
.filter { it.first != null && it.second != null }
.associateBy({ it.first!! }, { it.second!! })
println(map)
Upvotes: 3
Reputation: 6381
You want to filter out null
values, then you should use filter
method:
val map = listOf(Pair("a", 1), Pair("b", null), Pair("c", 3), Pair("d", null))
.filter { it.second != null }
.toMap()
println(map)
Upvotes: 9