Skip to content

Instantly share code, notes, and snippets.

@cy6erGn0m
Created May 20, 2015 14:41
Show Gist options
  • Save cy6erGn0m/97ecdc7191364572a94a to your computer and use it in GitHub Desktop.
Save cy6erGn0m/97ecdc7191364572a94a to your computer and use it in GitHub Desktop.
Merge two maps with custom reduce function for Kotlin
private fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { a, b -> b }): Map<K, V> {
val result = LinkedHashMap<K, V>(this.size() + other.size())
result.putAll(this)
other.forEach { e ->
val existing = result[e.key]
if (existing == null) {
result[e.key] = e.value
}
else {
result[e.key] = reduce(e.value, existing)
}
}
return result
}
@petrasrazanskas
Copy link

Same thing much more compactly.

fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { a, b -> b }): Map<K, V> {
    val result = LinkedHashMap<K, V>(this.size + other.size)
    result.putAll(this)
    other.forEach { e -> result[e.key] = result[e.key]?.let { reduce(e.value, it) } ?: e.value }
    return result
}

@golonzovsky
Copy link

golonzovsky commented May 29, 2017

maybe something like:

    fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { _, b -> b }): Map<K, V> =
            this.toMutableMap().apply { other.forEach { merge(it.key, it.value, reduce) } }

@vickychijwani
Copy link

Neat little function! I find the following to be a good balance between readability and conciseness (basically a slightly expanded version of @petrasrazanskas's code to reduce the complexity and line length of the other.forEach... statement):

fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { a, b -> b }): Map<K, V> {
    val result = LinkedHashMap<K, V>(this.size + other.size)
    result.putAll(this)
    for ((key, value) in other) {
        result[key] = result[key]?.let { reduce(value, it) } ?: value
    }
    return result
}

@giordy
Copy link

giordy commented Jul 18, 2018

I would suggest one more alternative

fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V = { a, b -> b }): Map<K, V> =
    other.entries.fold(this.toMutableMap()) { acc, entry ->
        acc[entry.key] = acc[entry.key]?.let { reduce(entry.value, it) } ?: entry.value
        acc
    }

@alamothe
Copy link

alamothe commented Nov 17, 2018

Based on @vickychijwani's but using Java's merge:

fun <K, V> Map<K, V>.mergeReduce(other: Map<K, V>, reduce: (V, V) -> V): Map<K, V> {
	val result = LinkedHashMap<K, V>(this)
	for ((key, value) in other) {
		result.merge(key, value, reduce)
	}
	return result
}

@geoffreywiseman
Copy link

geoffreywiseman commented Oct 11, 2019

I really liked the compactness of what @golonzovsky proposed, but I wanted:

  1. support for merging multiple maps without multiple toMutableMap() calls
  2. an in-place option if I already had a mutable map

So:

fun <K, V> Map<K, V>.mergeReduce(vararg others: Map<K, V>, reduce: (V, V) -> V): Map<K, V> =
		this.toMutableMap().apply { others.forEach { other -> other.forEach { merge(it.key, it.value, reduce) } } }

fun <K, V> MutableMap<K, V>.mergeReduceInPlace(vararg others: Map<K, V>, reduce: (V, V) -> V) =
		others.forEach { other -> other.forEach { merge(it.key, it.value, reduce) } }

@Woodz
Copy link

Woodz commented Oct 17, 2022

For those who are interested, this method is provided as part of https://github.com/LukasForst/katlib

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment