filterNotNull for Map values


#1
  val map: Map<Int, String> = mapOf(1 to "1", 2 to "2")
  val map2: Map<Int, String?> = map.mapValues { if (it.key == 1) "1" else null }
  
  @Suppress("UNCHECKED_CAST")
  val map3: Map<Int, String> = map2.filterValues { it != null } as Map<Int, String>

Is there a good way to solve this without the cast, or can one be added?


#2

I had some fun with this.

First, I think this cast is perfectly fine there.

This removes the cast, but at the cost of an additional intermediary data structure:

val map3: Map<Int, String> = map2.filterValues { it != null }.mapValues { it.value ?: "" }

Of course, you could go imperative. The closest “functionalish imperative” solution I came to is:

val map3: Map<Int, String>
    = hashMapOf<Int, String>().apply { map2.forEach { it.value?.apply { this.put(it.key, this) } } }

But this does not compile because I don’t know how to work with nested “receivers functions” (this in this.put should refer to the hashmap we just created, while the parameter this should refer to it.value).

Anyone knows how to deal with nested “receivers functions”? I tried this@HashMap to no avail.
Isn’t there something like this:

fun <T, R> with(e: T, f: (T) -> R): R

All such function in the standard libraries seem to involve a receiver.


Two other possible fixes that involve libraries changes:

  • add a filter method that can remove entries:

      fun <K1, V1, K2, V2> Map<K1, V1>.filterMap(f: (Map.Entry<K1, V1>) -> Map.Entry<K2, V2>?): Map<K2, V2>
    
  • using the Java8 stream API (not ported to Kotlin), you’d write something like

      val map3: Map<Int, String> = map2.stream.filterValues { it != null }.mapValues { it.value ?: "" }.toMap()
    

It’s very similar to the very first solution I proposed, but here you wouldn’t allocate an intermediary data structure.


`it` within `when`
#3

I guess this was more a feature request than a question. In the meantime I solved this by hiding the cast behind my own extension function.


#4

Looks like KT-4734 filterNotNullValues and filterNonNullKeys