I'm learning Kotlin Multiplatform and am trying to migrate a toy project of mine that was originally written for JVM, to Kotlin Native. One thing I'm stuck on is using the Java method Map.merge:
@Test
fun usingMerge() {
val map = mutableMapOf("A" to 42, "B" to 13)
map.merge("A", 20, ::max)
map.merge("B", 15, ::max)
map.merge("C", 10, ::max)
val expected = mapOf("A" to 42, "B" to 15, "C" to 10)
assertEquals(expected, map)
}
Since this Java method is not available in Kotlin Native, I'm trying to find a suitable replacement. The code I've come up with is too verbose and inefficient:
@Test
fun withoutMerge() {
val map = mutableMapOf("A" to 42, "B" to 13)
map["A"].also { if (it == null || it < 20) map["A"] = 20 }
map["B"].also { if (it == null || it < 15) map["B"] = 15 }
map["C"].also { if (it == null || it < 10) map["C"] = 10 }
val expected = mapOf("A" to 42, "B" to 15, "C" to 10)
assertEquals(expected, map)
}
Is there a way to write this that is shorter (approaching the conciseness of merge
), that does not have repetition (for example, "A" and 20 are repeated twice in the code above), and that does not do two lookups of the same key?
CodePudding user response:
You could just write your own merge as a pure kotlin extension function on the MutableMap interface.
This works for your example, it gets a bit more complex with generics of course:
fun MutableMap<String, Int>.merge2(k: String, v: Int, f: (Int, Int)->Int) {
if (this.containsKey(k)){
this[k]=f(this.getValue(k), v)
} else {
this[k]=v
}
}
fun main() {
val map = mutableMapOf("A" to 42, "B" to 13)
map.merge2("A", 20, Math::max)
map.merge2("B", 15, Math::max)
map.merge2("C", 10, Math::max)
}
CodePudding user response:
fun <T, U> MutableMap<T, U>.mergeWith(key: T, value: U, fn: (U, U) -> U) {
val v = this[key]
this[key] = if (v == null) value else fn(v, value)
}
val map = mutableMapOf("A" to 42, "B" to 13)
map.mergeWith("A", 20, Math::max)
map.mergeWith("B", 15, Math::max)
map.mergeWith("C", 10, Math::max)
map.forEach(::println)