Elye
Elye

Reputation: 60081

What's the equivalent of `mapNotNull` that result in map?

I can convert a List<Int?> to List<Int> using mapNotNull function as shown below.

    @Test
    fun main() {
        val testData = listOf(1, null, 3, null)
        val noNull = processAwayNull(testData)
    }

    private fun processAwayNull(testData: List<Int?>): List<Int> {
        return testData.mapNotNull{ it }
    }

How could I convert Map<String, Int?> to Map<String, Int>? The below with testData.filter { it.value != null } doesn't works, as it still produce Map<String, Int?>.

    @Test
    fun main() {
        val testData = mapOf("One" to 1, "Two" to null, "Three" to 3, "Four" to null)
        val noNull = processAwayNull(testData)
    }

    private fun processAwayNull(testData: Map<String, Int?>): Map<String, Int> {
        return testData.filter { it.value != null }
    }

Upvotes: 2

Views: 1450

Answers (2)

Vadzim
Vadzim

Reputation: 26170

Possible alternative with custom helper function:

inline fun <K, V, R> Map<K, V>.mapValuesNotNullToMap(transformValue: (V) -> R?): Map<K, R> =
    buildMap {
        [email protected] { (key, value) ->
            transformValue(value)?.let { put(key, it) }
        }
    }

Upvotes: 0

Roland
Roland

Reputation: 23242

Well, not really out of the box (in the sense that you get Map<String, Int> immediately), but what about filterValues?

testData.filterValues { it != null } // gives Map<String, Int?> but without null-values

Combining or replacing that with mapValues (maybe you can use a default value instead of null?):

// combining:
testData.filterValues { it != null }
        .mapValues { (_, value) -> value as Int }
// replacing:
testData.mapValues { (_, value) -> value ?: /* default value */ 0 } 

Both give a Map<String, Int> but the first creates and fills 2 maps under the hood and the second uses 0 instead of null.

You can also simplify the filterValues-variant with an appropriate unchecked cast, as "we know it better":

testData.filterValues { it != null } as Map<String, Int> // unchecked cast, because: we really do know better, do we? ;-)

Alternatively, you could also just handle all entries the way you knew already (using mapNotNull) and then create a new map out of it:

testData.asSequence()
        .mapNotNull { (key, value) ->
            value?.let {
              key to it
            }
        }
        .toMap() // giving Map<String, Int>

If you require that more often you may even want to have your own extension function in place:

@Suppress("UNCHECKED_CAST")
fun <K, V> Map<K, V?>.filterValuesNotNull() = filterValues { it != null } as Map<K, V>

Now you can use it similar as to follows:

testData.filterValuesNotNull() // giving Map<String, Int>

Upvotes: 5

Related Questions