I need merge maps mapA
andmapB
with pairs of "name" - "phone number" into the final map, sticking together the values for duplicate keys, separated by commas. Duplicate values should be added only once.
I need the most idiomatic and correct in terms of language approach.
For example:
val mapA = mapOf("Emergency" to "112", "Fire department" to "101", "Police" to "102")
val mapB = mapOf("Emergency" to "911", "Police" to "102")
The final result should look like this:
{"Emergency" to "112, 911", "Fire department" to "101", "Police" to "102"}
This is my function:
fun mergePhoneBooks(mapA: Map<String, String>, mapB: Map<String, String>): Map<String, String> {
val unionList: MutableMap <String, String> = mapA.toMutableMap()
unionList.forEach { (key, value) -> TODO() } // here's I can't come on with a beatiful solution
return unionList
}
How about:
val unionList = (mapA.asSequence() + mapB.asSequence())
.distinct()
.groupBy({ it.key }, { it.value })
.mapValues { (_, values) -> values.joinToString(",") }
Result:
{Emergency=112,911, Fire department=101, Police=102}
This will:
Sequence
of both maps' key-value pairsMap<String, List<String>
)Map<String, String>
)You can do the following:
(mapA.keys + mapB.keys).associateWith {
setOf(mapA[it], mapB[it]).filterNotNull().joinToString()
}
joinToString()
.While I looked at the other solutions I couldn't believe that there isn't an easier way (or ways as easy as the accepted answer without the need to recreate a Map
, intermediate new lists, etc.). Here are 3 (of many ;-)) solutions I came up with:
Using the keys and mapping the values later:
(mapA.keys.asSequence() + mapB.keys)
.associateWith {
sequenceOf(mapA[it], mapB[it]) // one of the sides may have null values in it (i.e. no entry in the map)...
.filterNotNull()
.distinct()
.toList() // or if you require/prefer, do the following instead: joinToString()
}
Using groupingBy
and fold
(or have a look at: Group by key and fold each group simultaneously (KEEP)):
(mapA.asSequence() + mapB.asSequence())
.groupingBy { it.key }
.fold({ _, _ -> mutableSetOf() }) { _, accumulator, element ->
accumulator.apply {
add(element.value)
}
}
You could also just use an empty String
instead and concatenate in the fold operation the way you need it. My first approach just used a sequenceOf
instead of the MutableSet
. It depends on what you require and what you want to do with the result afterwards. Be sure to use the overloaded fold
-function that accepts an initial value selector, which creates a new initial value every time a new key is encountered. Thanks xzt for noting it.
Using Javas Map.merge
, but ignoring duplicates in the value and also just concatenating the values:
val mergedMap: Map<String, String> = mapA.toMutableMap().apply {
mapB.forEach { key, value ->
merge(key, value) { currentValue, addedValue ->
"$currentValue, $addedValue" // just concatenate... no duplicates-check..
}
}
}
This, of course, can also be written differently, but this way we ensure that mergedMap is still just a Map<String, String>
when accessed again.