Java 合并地图 kotlin 中的值

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/54232530/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 01:00:00  来源:igfitidea点击:

Merge values in map kotlin

javakotlin

提问by A.Rost

I need merge maps mapAandmapBwith pairs of "name" - "phone number" into the final map, sticking together the values for duplicate keys, separated by commas. Duplicate values should be added only once. I need the most idiomatic and correct in terms of language approach.

我需要将地图mapAmapB“姓名”-“电话号码”对合并到最终地图中,将重复键的值粘在一起,用逗号分隔。重复值只能添加一次。我需要最惯用和正确的语言方法。

For example:

例如:

val mapA = mapOf("Emergency" to "112", "Fire department" to "101", "Police" to "102")
val mapB = mapOf("Emergency" to "911", "Police" to "102")

The final result should look like this:

最终结果应如下所示:

{"Emergency" to "112, 911", "Fire department" to "101", "Police" to "102"}

This is my function:

这是我的功能:

fun mergePhoneBooks(mapA: Map<String, String>, mapB: Map<String, String>): Map<String, String> {
    val unionList: MutableMap <String, String> = mapA.toMutableMap()
    unionList.forEach { (key, value) -> TODO() } // here's I can't come on with a beatiful solution

    return unionList
}

采纳答案by Mtheitroada

How about:

怎么样:

val unionList = (mapA.asSequence() + mapB.asSequence())
    .distinct()
    .groupBy({ it.key }, { it.value })
    .mapValues { (_, values) -> values.joinToString(",") }

Result:

结果:

{Emergency=112,911, Fire department=101, Police=102}

This will:

这会:

  • produce a lazy Sequenceof both maps' key-value pairs
  • group them by key (result: Map<String, List<String>)
  • map their values to comma-joined strings (result: Map<String, String>)
  • 生成Sequence两个映射的键值对的惰性
  • 由密钥组它们(结果:Map<String, List<String>
  • 它们的值映射到逗号接合字符串(结果:Map<String, String>

回答by Feedforward

I would write something like

我会写类似的东西

fun Map<String, String>.mergeWith(another: Map<String, String>): Map<String, String> {
  val unionList: MutableMap<String, String> = toMutableMap()
  for ((key, value) in another) {
    unionList[key] = listOfNotNull(unionList[key], value).toSet().joinToString(", ")
  }
  return unionList
}

val mergedMap = mapA.mergeWith(mapB)

回答by Ilya E

    val mapA = mapOf("Emergency" to "112", "Fire department" to "101", "Police" to "102")
    val mapB = mapOf("Emergency" to "911", "Police" to "102")

    val result = (mapA.entries + mapB.entries)
        .groupBy({ it.key }, { it.value })
        .mapValues {(_, value) -> 
            value.joinToString(", ")
        }

回答by Sergey

Another approach:

另一种方法:

val mapA = mapOf("Emergency" to "112", "Fire department" to "101", "Police" to "102")
val mapB = mapOf("Emergency" to "911", "Police" to "102")

val result = mapA.toMutableMap()
mapB.forEach {
    var value = result[it.key]
    value = if (value == null || value == it.value) it.value else value + ", ${it.value}"
    result[it.key] = value
}

Or using infix extension function:

或使用中缀扩展功能

infix fun Map<String, String>.mergeWith(anotherMap: Map<String, String>): Map<String, String> {
    val result = this.toMutableMap()
    anotherMap.forEach {
        var value = result[it.key]
        value = if (value == null || value == it.value) it.value else value + ", ${it.value}"
        result[it.key] = value
    }
    return result
}

val result = mapA mergeWith mapB

回答by s1m0nw1

Here's my solution:

这是我的解决方案:

val result = (mapA + (mapB - mapA.keys)).mapValues {
    (setOf(it.value) + mapB[it.key]).filterNotNull().joinToString()
}

It creates a map of A plus the values from B that are not in A. Then it maps all values to a set and adds the value from B to that set, ultimately removing all nullvalues from the set and transforming it into a list, which you can use to create the desired output format.

它创建了一个 A 的映射加上 B 中不在 A 中的值。然后它将所有值映射到一个集合,并将 B 中的值添加到该集合中,最终null从集合中删除所有值并将其转换为一个列表,其中您可以使用它来创建所需的输出格式。

回答by Roland

While I looked at the other solutions I couldn't believe that there isn't an easier way (or ways as easy as the accepted answer without the need to recreate a Map, intermediate new lists, etc.). Here are 3 (of many ;-)) solutions I came up with:

当我查看其他解决方案时,我无法相信没有更简单的方法(或者像接受的答案一样简单的方法,而无需重新创建Map中间新列表等)。这是我想出的 3 个(许多 ;-))解决方案:

  1. Using the keys and mapping the values later:

    (mapA.keys.asSequence() + mapB.keys)
        .associateWith {
          sequenceOf(mapA[it], mapB[it]) // one of the sides may have null values in it (i.e. no entry in the map)...
              .filterNotNull()
              .distinct()
              .toList() // or if you require/prefer, do the following instead: joinToString()
        }
    
  2. Using groupingByand fold(or have a look at: Group by key and fold each group simultaneously (KEEP)):

    (mapA.asSequence() + mapB.asSequence())
      .groupingBy { it.key }
      .fold(mutableSetOf<String>()) { accumulator, element ->
        accumulator.apply {
          add(element.value)
        }
      }
    

    You could also just use an empty Stringinstead and concatenate in the fold operation the way you need it. My first approach just used a sequenceOfinstead of the MutableSet. It depends on what you require and what you want to do with the result afterwards.

  3. Using Javas Map.merge, but ignoring duplicates in the value and also just concatenating the values:

    val mergedMap: Map<String, String> = mapA.toMutableMap().apply {
      mapB.forEach { key, value ->
        merge(key, value) { currentValue, addedValue ->
          "$currentValue, $addedValue" // just concatenate... no duplicates-check..
        }
      }
    }
    

    This, of course, can also be written differently, but this way we ensure that mergedMap is still just a Map<String, String>when accessed again.

  1. 稍后使用键并映射值:

    (mapA.keys.asSequence() + mapB.keys)
        .associateWith {
          sequenceOf(mapA[it], mapB[it]) // one of the sides may have null values in it (i.e. no entry in the map)...
              .filterNotNull()
              .distinct()
              .toList() // or if you require/prefer, do the following instead: joinToString()
        }
    
  2. 使用groupingByfold(或查看:按键分组并同时折叠每组(保持)):

    (mapA.asSequence() + mapB.asSequence())
      .groupingBy { it.key }
      .fold(mutableSetOf<String>()) { accumulator, element ->
        accumulator.apply {
          add(element.value)
        }
      }
    

    您也可以只使用一个空的String,并按照您需要的方式在折叠操作中连接。我的第一种方法只是使用 asequenceOf而不是MutableSet. 这取决于您需要什么以及您想对结果做什么。

  3. 使用 Javas Map.merge,但忽略值中的重复项,也只是连接值:

    val mergedMap: Map<String, String> = mapA.toMutableMap().apply {
      mapB.forEach { key, value ->
        merge(key, value) { currentValue, addedValue ->
          "$currentValue, $addedValue" // just concatenate... no duplicates-check..
        }
      }
    }
    

    当然,这也可以写成不同的,但是这样我们可以确保mergedMapMap<String, String>在再次访问时仍然只是a 。

回答by Willi Mentzel

You can do the following:

您可以执行以下操作:

(mapA.keys + mapB.keys).associateWith {
    setOf(mapA[it], mapB[it]).filterNotNull().joinToString()
}
  1. put all keys in a set
  2. iterate over that set and and associate each element with the set of values
  3. remove the null values from the value set
  4. concatenate the elements in the value list using joinToString().
  1. 把所有的钥匙放在一个集合中
  2. 迭代该集合并将每个元素与值集相关联
  3. 从值集中删除空值
  4. 使用 连接值列表中的元素joinToString()

回答by Anton Kushch

In Kotlin you could do this:

在 Kotlin 中,你可以这样做:

fun main() {
    val map1 = mapOf("A" to 1, "B" to 2)
    val map2 = mapOf("A" to 5, "B" to 2)
    val result: Map<String, Int> = listOf(map1, map2)
        .fold(mapOf()) { accMap, map ->
            accMap.merge(map, Int::plus)
        }
    println(result) // Prints: {A=6, B=4}
}

private fun <T, V> Map<T, V>.merge(another: Map<T, V>, mergeFunction: (V, V) -> V): Map<T, V> =
    toMutableMap()
        .apply {
            another.forEach { (key, value) ->
                merge(key, value, mergeFunction)
            }
        }

回答by Vadzim

Here is my approach with universal map-merging helper function:

这是我使用通用地图合并辅助函数的方法:

fun <K, V, R> Pair<Map<K, V>, Map<K, V>>.merge(merger: (V?, V?) -> R): Map<K, R> {
    return (first.keys.asSequence() + second.keys.asSequence())
            .associateWith { merger(first[it], second[it]) }
}

fun main() {
    val mapA = mapOf("Emergency" to "112", "Fire department" to "101", "Police" to "102")
    val mapB = mapOf("Emergency" to "911", "Police" to "102")
    val result = (mapA to mapB).merge { a, b -> 
            listOf(a, b).filterNotNull().distinct().joinToString(",", "(", ")") }
    println(result)
}

Output:

输出:

{Emergency=(112,911), Fire department=(101), Police=(102)}

{紧急=(112,911),消防部门=(101),警察=(102)}