Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how to elegantly create a map with only non-null values in Kotlin

Tags:

kotlin

How can I rewrite this code without having to resort to a MutableMap and conversion to immutable map?

fun createMap(mandatoryValue: String, optionalValue: String?): Map<String, String> {
    val map = mutableMapOf("key1" to mandatoryValue)
    optionalValue?.let { map.put("key2", it) }
    return map.toMap()
}

My alternative solution isn't very nice either because it needs an unsafe cast:

fun createMap(mandatoryValue: String, optionalValue: String?): Map<String, String> =
    mapOf(
        "key1" to mandatoryValue,
        "key2" to optionalValue
    ).filterValues { it != null } as Map<String, String>

What I am looking for is something in the lines of:

fun createMap(mandatoryValue: String, optionalValue: String?): Map<String, String> =
    mapOf(
        "key1" to mandatoryValue,
        optionalValue?.let { "key2" to it }
    )
like image 593
Paul S Avatar asked Oct 17 '25 10:10

Paul S


2 Answers

toMap() does not necessarily create an immutable map. It is only guaranteed to be read-only. The underlying class instance might be a MutableMap (which in the current implementation is true if it has more than one key). Therefore, toMap() in your first block of code is unnecessary. The MutableMap is automatically upcast to Map when you return it since you specified Map as the return type. So, you could have put

fun createMap(mandatoryValue: String, optionalValue: String?): Map<String, String> {
    val map = mutableMapOf("key1" to mandatoryValue)
    optionalValue?.let { map.put("key2", it) }
    return map
}

or

fun createMap(mandatoryValue: String, optionalValue: String?): Map<String, String> =
    mutableMapOf("key1" to mandatoryValue).apply {
        if (optionalValue != null) put("key2", optionalValue)
    }

To get the syntax you requested in your last example, you could create an overload of mapOf that accepts and filters null values:

fun <K, V> mapOf(vararg pairs: Pair<K, V>?): Map<K, V> =
    mapOf(*pairs.filterNotNull().toTypedArray())
like image 125
Tenfour04 Avatar answered Oct 20 '25 16:10

Tenfour04


You can have your own extension function as follows and then use it to filter null values from a Map:

fun <K, V> Map<K, V?>.filterValuesNotNull() = 
    mapNotNull { (k, v) -> v?.let { k to v } }.toMap()
like image 33
João Dias Avatar answered Oct 20 '25 15:10

João Dias