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?>
toMap throws a NullPointerException if one of the values is null .
It doesn't allow nulls. So consider using a TreeMap when you want a Map sorts its key-value pairs by the natural order of the keys. Points to remember: Map doesn't allow duplicate keys, but it allows duplicate values.
If you pass null as map key, it will go to 0 bucket . All values of null key will go there. That is why it returns same value, cause all keys you are providing are null and are in the same bucket of your HashMap.
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
.
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)
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With