Issue
Does anyone know how to convert a List<Pair<K, Collection< V >>> to Map<K,Collection< V >> in Kotlin, merging the collections associated with the same key?
In Java I would use something like:
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (c1, c2) -> {
List result = new ArrayList<>();
result.addAll(c1);
result.addAll(c2);
return result;
})
Does Kotlin have a similar device, or a better one? Any advice is appreciated.
Solution
FYI, Kotlin has a toMap function exactly for that in cases where you don't need to merge the values of duplicate keys.
However, if you want to merge, you'll have to go with slightly more complicated. Assuming you have a list of pairs like this:
val pairs = listOf(
1 to listOf("1", "2", "3"),
1 to listOf("4"),
2 to listOf("1", "2", "3"),
3 to listOf("1", "2", "3"),
3 to listOf("4", "5", "6"),
)
You can use groupBy and mapValues like this:
val map = pairs.groupBy({ it.first }, { it.second })
.mapValues { (k, v) -> v.flatten() }
The groupBy
will create a Map<Int, List<List<String>>>
, so each key will be mapped to the list of all the values corresponding to it. Then the mapValues
flattens each list of lists so it becomes a single list.
Note that this create an intermediate map and may not be very efficient in performance-sensitive code compared to the provided Java. If you need efficiency, you can use groupingBy instead to get a more lazy grouping and aggregate it as you please. For instance:
val map = pairs.groupingBy { it.first }
.fold(emptyList<String>()) { acc, v -> acc + v.second }
which is nice but creates intermediate lists when merging. Or:
val map = pairs.groupingBy { it.first }.fold(
initialValueSelector = { _, _ -> mutableListOf<String>() },
operation = { _, acc, v -> acc.addAll(v.second); acc }
)
which creates a single list per key, but is a bit more complicated. Or another variant if you want to get crazier:
val map = pairs.groupingBy { it.first }.aggregate { k, acc: List<String>?, v, _ ->
if (acc == null) v.second else acc + v.second
}
(which admittedly is a bit of a mouthful, and still creates intermediate lists)
Answered By - Joffrey
Answer Checked By - Pedro (JavaFixing Volunteer)