合并具有相似外部键的嵌套映射,保留两个内部映射的键值对

Merge nested maps with similar outer keys, keeping both inner map's key value pairs

我想按照下面的方式合并两个嵌套地图,而不覆盖外部地图值的数据,或者通过替换内部地图并覆盖已经存在的数据。

理想情况下,在嵌套地图中,我会保留我已有的 key/value 对,并在第二张地图中添加。

    val mapOne: MutableMap<String, MutableMap<String, String>>  = mutableMapOf(
        "DirectoryOne" to  mutableMapOf(
            "FeatureOne" to "SomeUniqueStringFeature1",
            "FeatureTwo" to "SomeUniqueStringFeature2"
        )
    )
    val mapTwo: MutableMap<String, MutableMap<String, String>> = mutableMapOf(
        "DirectoryOne" to  mutableMapOf(
            "FeatureThree" to "SomeUniqueStringFeature3"
        )
    )

我需要的结果如下所示:

    val mapOne: MutableMap<String, MutableMap<String, String>>  = mutableMapOf(
        "DirectoryOne" to  mutableMapOf(
            "FeatureOne" to "SomeUniqueStringFeature1",
            "FeatureTwo" to "SomeUniqueStringFeature2",
            "FeatureThree" to "SomeUniqueStringFeature3"
        )
    )

只是为了详细说明这背后的一些原因。我计划在运行时动态构建地图,并将功能添加到单个地图,因为它们需要在整个应用程序持续时间内添加。

我能够使用以下代码执行所需的操作。迭代外部 key/value 对并将嵌套的 key/value 对与 BiConsumer/BiFunction 合并到 mapOne。 (打字有点累)

mapTwo.forEach(BiConsumer { k: String, v: MutableMap<String, String> ->
        mapOne.merge(k, v,
            BiFunction { v1: MutableMap<String, String>, v2: MutableMap<String, String> ->
                v1.putAll(v2)
                v1
            })
    })

我很快发现我可以更轻松地使用 lambda(删除 BiConsumer/BiFunction),然后将 lambda 移出合并括号。最终最优解如下图

mapTwo.forEach { (k: String, v: MutableMap<String, String>) ->
        mapOne.merge(k, v
        ) { v1: MutableMap<String, String>, v2: MutableMap<String, String> ->
            v1.putAll(v2)
            v1
        }
    }

我将上面的代码片段包装在 function/method 中,以便能够在需要的地方多次应用它。

如果想避免Java8合并功能的尴尬使用,可以使用getOrPut。由于函数内联,这也更有效。

此外,根据 Kotlin coding conventions,最好使用常规 for 循环而不是 forEach(除非您在可空对象上或在集合运营商链)。

for ((key, map) in mapTwo) {
    mapOne.getOrPut(key, ::mutableMapOf).putAll(map)
}

广义:

fun <K1, K2, V> MutableMap<in K1, MutableMap<K2, V>>.mergeIn(other: Map<out K1, Map<out K2, V>>) {
    for ((key, map) in other) {
        getOrPut(key, ::mutableMapOf).putAll(map)
    }
}