Hi guys, the stdlib `fun List<Pair<K,V>&g...
# announcements
j
Hi guys, the stdlib
fun List<Pair<K,V>>.toMap()
drops duplicate entries without providing a way to merge them. Is there a stdlib function that can do this? I often need this when dealing with maps containing quantities. I implement it this way:
Copy code
fun <K, V> List<Pair<K, V>>.toMap(combine: (V, V) -> V?): Map<K, V> {
    val result = mutableMapOf<K, V>()
    for ((k, v) in this) {
        result.merge(k, v, combine)
    }
    return result
}
Another one I'm missing is merging 2 maps provided a function to recombine the values of keys that are present in both maps.
m
groupBy
j
But
groupBy
only groups values in lists, it doesn't
reduce
these lists, so it is still quite cumbersome to use it:
Copy code
fun <K, V> List<Pair<K, V>>.toMap(combine: (V, V) -> V): Map<K, V> =
        groupBy({ it.first }, { it.second})
        .mapValues { it.value.reduce { v1, v2 -> combine(v1, v2) } }
Or am I missing something here?
m
merge functions do indeed not exist (yet). I had this problem before: https://discuss.kotlinlang.org/t/associate-associateby-merge-strategy-default/2849
seems like merge strategies are hard to implement without affecting performance
I would go for a groupingBy strategy combined with a fold for directly working with your values.
j
Nice thread, and nice point you raised then, thanks for the link! However, I'm not asking for a change in `toMap()`'s default. I'm just asking for another stdlib function, like an overload of
toMap()
to actually allow the users to avoid reimplementing a mix of
groupBy
+
reduce
for such a common use case. The critical point here is: is that a really common use case, or am I the only one who needs to merge things? Also, seeing this overload in the autocomplete would probably make the users think of "Oh yeah, what if there are duplicates?"
m
I completely agree btw, seems like more people are having our problem
👍 1
Copy code
map.groupingBy { it.first }.fold(null) { accumulator: Int?, element -> merge(element.second, accumulator) }
I would do it like this if you have no default value for your merging strategy (or 0 if that's fine too)
j
I find Kotlin so nice to read in general, I'm just sad this use case needs to let multiple operations surface in user code, when what we want to express is a single concept/step.
Also, your example is not very general since you're using
Int
here. I'd rather implement the utility function from my original post, which also needs to allocate way less objects in the end.
m
Int was an example, you'd ofcourse use V?. But groupingBy in general is better performance in general over groupBy. groupBy makes a map immediatly where you would map values (reducing a list to a singular value). groupingBy with a collector would do exactly would you want: merge only on the two given values
j
Indeed, altough
groupingBy
still creates an intermediate
Grouping
instance, but I have to admit it's already better. Anyway, I can also express the initial function directly with the following:
Copy code
fun <K, V> List<Pair<K, V>>.toMap(combine: (V, V) -> V?): Map<K, V> =
        fold(mutableMapOf<K, V>()) { m, p -> m.merge(p.first, p.second, combine); m }
Using
fold
directly solves the problem of intermediate stuff, but it's still a bit overkill, because reading that is not very nice to my eye 🙂
1
m
That's also possible. Only addition I would propose is to make your function for an Iterable, instead of a list, so it would work with other collections
👍 2
j
Yes of course, that would be better. Anyway, thanks for taking the time to discuss it with me, I wish there were direct support for this in the stdlib. I don't know if such additions should be discussed here, or in the forum, or if people usually directly create formal KEEPs
m
There's #CQ3GFJTU1 for general discussion and #C0B9K7EP2 for discussing a KEEP. So perhaps start in #CQ3GFJTU1 and get a feel for what people in that group think/suggest. For now, obviously you can create your function as an extension so until (if) it appears in the stdlib, at least you have a nice name for it.
👍 1
j
Thanks a lot