Reputation: 2313
I have a mutable map (a LinkedHashMap
to be specific) in Kotlin. For each map entry, I want to conditionally perform one of three actions:
In old Java, I would probably use an Iterator
over the map's entrySet()
for this purpose (using Iterator.remove()
or Entry.setValue()
accordingly). However, I wondered if there is a more elegant, functional approach present in Kotlin to do the same (i.e. apply a lambda to each entry in-place, similar to using replaceAll()
, but with the ability to also remove entries).
In Kotlin, the following combination of replaceAll()
and retainAll()
does the same thing:
val map = LinkedHashMap<String,Int>(mapOf("a" to 1, "b" to 2, "c" to 3))
map.replaceAll { key, value ->
if(key.startsWith("a")) {
value * 2
} else {
value
}
}
map.entries.retainAll { entry ->
entry.key.startsWith("a") || entry.key.startsWith("b")
}
However, this iterates the map twice and requires splitting up the predicate/transformation. I would prefer a compute()
-style all-in-one solution, just for all entries.
Upvotes: 12
Views: 16333
Reputation: 5513
In functional world, immutability is an essential principle. So in-place modifications are not convenient.
But if you prefer an in-place approach, possible options are retainAll
or removeAll
to filter items. For in-place replacement you need to iterate the collection. You can use forEach
to do an in-place modification:
data class User(var name: String)
val users = mutableListOf( User("John"), User("Alice"), User("Bob") )
users.run {
removeAll { it.name == "Alice" }
forEach { it.name = "Mr. ${it.name}"}
}
Upvotes: 4
Reputation: 461
This version is in place (similar to what would you write in java) but not with functional approach
val map: MutableMap<String, Int> = mutableMapOf(
("a" to 1),
("b" to 2),
("c" to 3),
)
val mapIterator = map.iterator()
while (mapIterator.hasNext()) {
val mapEntry = mapIterator.next()
when (mapEntry.key) {
"a" -> mapIterator.remove()
"c" -> mapEntry.setValue(mapEntry.value * 2)
else -> println("retain this entry")
}
}
Upvotes: 2
Reputation: 5207
Here is an example how you can do all operations in a single expression:
filter{}
we keep only elements that fit particular conditions, here: with a value >= 200. The result of this filter is a new map.toMap()
we transform the resulting collection into a new map.fun main(args: Array<String>) {
val map = mutableMapOf<String, Int>()
map.put("a1", 101)
map.put("a2", 102)
map.put("b1", 201)
map.put("b2", 202)
map.put("c1", 301)
map.put("c2", 302)
println(map)
val result = map.filter { (key, value) -> value >= 200 }.
map { (key,value) ->
if (value >= 300) Pair(key, value * 100)
else Pair(key, value)
}.toMap()
println(result)
}
Upvotes: 4
Reputation: 8442
For example, you can write like this
fun main() {
val map = mapOf(
"test1" to 1,
"test2" to 2,
"test3" to 3
)
val resultMap = map.mapNotNull { (key, value) ->
when (value) {
1 -> null // remove
2 -> key to 4 // replace
else -> key to value // retain
}
}.toMap()
println(resultMap) // print {test2=4, test3=3}
}
Upvotes: 2