How to use withDefault wrappers?
I've been looking for a way to return default value from a MutableMap, but also store it at the same time for future retrieval. The .withDefault
only returns the default value, but doesn't store it. Calling .getOrPut
every time I need to retrieve a value doesn't look like a good idea. I've came up with something like this:
val myMap = with(mutableMapOf<String, Set<String>>()) {
withDefault { key -> getOrPut(key, { mutableSetOf<String>() }) }
}
This calls getOrPut
within withDefault
wrapper on the backing MutableMap object, which puts the missing key-value pair into the map and returns it.
To encapsulate the behaviour you can create a delegated property provider.
/** Wraps a [MutableMap]. Will generate a [defaultValue] if none exists, and set it into the map. */
fun <K, V> mapWithPutDefault(defaultValue: (key: K) -> V): ReadWriteProperty<Any?, MutableMap<K, V>> =
object : ReadWriteProperty<Any?, MutableMap<K, V>> {
private var map: MutableMap<K, V> = with(mutableMapOf<K, V>()) {
withDefault { key -> getOrPut(key) { defaultValue(key) } }
}
override fun getValue(thisRef: Any?, property: KProperty<*>): MutableMap<K, V> = map
override fun setValue(thisRef: Any?, property: KProperty<*>, value: MutableMap<K, V>) {
this.map = value
}
}
fun main() {
val myMap: MutableMap<String, String> by mapWithPutDefault { "some default value for $it" }
println("map is empty: $myMap")
// map is empty: {}
val someValue = myMap.getValue("my-key")
println("map returns a default value: $someValue")
// map returns a default value: some default value for my-key
println("map now has a value: $myMap")
// map now has a value: {my-key=some default value for my-key}
}
Looks like in Kotlin 1.1 this actually works if you use the getValue() function instead of the get()
function.
As of Kotlin 1.0 a wrapper returned by withDefault
is only usable in property delegation use cases.
val map = mutableMapOf<String, Set<String>>().withDefault { mutableSetOf() }
var property: Set<String> by map // returns empty set by default