Issue
I am trying to write a function that can merge a list of maps where the return value:
- contains no duplicate key/value pairs
- groups by key - for example, when there are multiple entries for the same key, all values for that key are combined as List
- all other values are String
I've gotten this far:
fun mergeMaps(maps: List<Map<String, *>>): Map<String, *> {
val mergedMap =
maps.reduce { acc, map ->
(acc.asSequence() + map.asSequence()).distinct().groupBy({ it.key }, { it.value })
}
return mergedMap.entries.fold(mutableMapOf<String, Any>()) { acc, entry ->
val entryValue = entry.value as List<String>
val key = entry.key
val value = if (entryValue.size > 1) entryValue else entryValue.first()
if (acc.containsKey(key)) {
when (acc[key]) {
is String -> {
acc[key] = listOf(acc[key], value)
}
else -> {
acc[key] = (acc[key] as List<String>) + value
}
}
} else acc[key] = value
acc
}
}
but I am struggling to get the function to handle existing list values.
val map1 = mapOf("foo" to "bar", "test" to "test-value", "arrayTest" to listOf("foo", "bar"))
val map2 = mapOf("biz" to "buz", "test" to "something-else", "arrayTest" to "baz")
val mergedMaps = mergeMaps(listOf(map1, map2))
yields
{foo=bar, test=[test-value, something-else], arrayTest=[[foo, bar], baz], biz=buz}
when I expect it to be:
{foo=bar, test=[test-value, something-else], arrayTest=[foo, bar, baz], biz=buz}
Is there a better way to do this? Where am I going wrong??
Solution
I think a simpler strategy is to first add everything into a Map with List values. I would expect this to more typically be the end-goal since it would be usable without casts, but assuming you have a use case that needs it the way you described, you can then replace the lists that have only one item using mapValues.
fun mergeMaps(maps: List<Map<String, *>>): Map<String, *> {
return maps.fold(mutableMapOf<String, MutableList<Any?>>()) { outMap, map ->
for ((key, value) in map) {
val outList = outMap.getOrPut(key, ::mutableListOf)
if (value is List<*>) outList.addAll(value) else outList.add(value)
}
outMap
}.mapValues { (_, value) -> if (value.size == 1) value.first() else value }
}
This is also written in a way where the value types can be anything besides List (not just String).
Answered By - Tenfour04
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.