合并地图Kotlin中的值

时间:2019-01-17 09:16:53

标签: java kotlin

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

例如:

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

最终结果应如下所示:

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

这是我的功能:

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
}

11 个答案:

答案 0 :(得分:5)

怎么样:

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

结果:

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

这将:

  • 产生两个地图的键值对的惰性Sequence
  • 按键将它们分组(结果:Map<String, List<String>
  • 将它们的值映射到逗号连接的字符串(结果:Map<String, String>

答案 1 :(得分:2)

我会写类似的东西

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)

答案 2 :(得分:2)

    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(", ")
        }

答案 3 :(得分:2)

您可以执行以下操作:

(mapA.keys + mapB.keys).associateWith {
    setOf(mapA[it], mapB[it]).filterNotNull().joinToString()
}
  1. 将所有密钥放入一组
  2. 遍历该集合并将每个元素与该值集合关联
  3. 从值集中删除空值
  4. 使用joinToString()连接值列表中的元素。

答案 4 :(得分:2)

在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)
            }
        }

答案 5 :(得分:1)

另一种方法:

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
}

或使用中缀扩展功能

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

答案 6 :(得分:1)

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

  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(或看看: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)
        }
      }
    

    您也可以只使用一个空的String并按照需要的方式在折叠操作中进行串联。我的第一种方法只是使用sequenceOf而不是MutableSet。这取决于您需要什么以及之后想要对结果做什么。

  3. 使用Java 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..
        }
      }
    }
    

    这当然也可以用不同的方式编写,但是通过这种方式,我们可以确保再次访问mergedMap时仍然只是Map<String, String>

答案 7 :(得分:1)

一种更通用的方法(如在搜索Kotlin和合并地图时出现此帖子):

fun <K, V1, V2, R> Map<K, V1>.mergeReduce(other: Map<K, V2>, reduce: (key: K, value1: V1?, value2: V2?) -> R): Map<K, R> =
    (this.keys + other.keys).associateWith { reduce(it, this[it], other[it]) }

它允许合并具有不同类型值的Map,通过自定义的reducer来增加自由度,并提高可读性。

您的问题可以解决为:

mapA.mergeReduce(mapB) { _, value1, value2 -> listOfNotNull(value1, value2).joinToString(", ") }

答案 8 :(得分:0)

这是我的解决方法:

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

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

答案 9 :(得分:0)

这是我使用通用地图合并助手功能的方法:

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)
}

输出:

  

{紧急情况=(112,911),消防队=(101),警察=(102)}

答案 10 :(得分:-1)

(mapA.data.asSequence() + mapB.asSequence())
   .map { Pair(it.key, it.value) }
   .toMap()