合并两个地图并对相同键的值求和的最佳方法? [英] Best way to merge two maps and sum the values of same key?

查看:28
本文介绍了合并两个地图并对相同键的值求和的最佳方法?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

我想合并它们,并对相同键的值求和.所以结果将是:

I want to merge them, and sum the values of same keys. So the result will be:

Map(2->20, 1->109, 3->300)

现在我有两个解决方案:

Now I have 2 solutions:

val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }

val merged = (map1 /: map2) { case (map, (k,v)) =>
    map + ( k -> (v + map.getOrElse(k, 0)) )
}

但我想知道是否有更好的解决方案.

But I want to know if there are any better solutions.

推荐答案

Scalaz 有一个 Semigroup 捕获你想在这里做什么,并导致可以说是最短/最干净的解决方案:

Scalaz has the concept of a Semigroup which captures what you want to do here, and leads to arguably the shortest/cleanest solution:

scala> import scalaz._
import scalaz._

scala> import Scalaz._
import Scalaz._

scala> val map1 = Map(1 -> 9 , 2 -> 20)
map1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 9, 2 -> 20)

scala> val map2 = Map(1 -> 100, 3 -> 300)
map2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 100, 3 -> 300)

scala> map1 |+| map2
res2: scala.collection.immutable.Map[Int,Int] = Map(1 -> 109, 3 -> 300, 2 -> 20)

具体来说,Map[K, V] 的二元运算符组合了映射的键,将 V 的半群运算符折叠到任何重复值上.Int 的标准半群使用加法运算符,因此您可以获得每个重复键的值之和.

Specifically, the binary operator for Map[K, V] combines the keys of the maps, folding V's semigroup operator over any duplicate values. The standard semigroup for Int uses the addition operator, so you get the sum of values for each duplicate key.

编辑:根据 user482745 的要求,提供更多细节.

Edit: A little more detail, as per user482745's request.

在数学上,semigroup 只是一组值,加上一个运算符,该运算符需要两个该集合中的值,并从该集合中产生另一个值.因此,加法运算的整数是一个半群,例如 - + 运算符将两个整数组合成另一个整数.

Mathematically a semigroup is just a set of values, together with an operator that takes two values from that set, and produces another value from that set. So integers under addition are a semigroup, for example - the + operator combines two ints to make another int.

您还可以在具有给定键类型和值类型的所有映射"集合上定义一个半群,只要您能想出一些将两个映射组合起来生成一个新映射的操作,这在某种程度上是两个输入的组合.

You can also define a semigroup over the set of "all maps with a given key type and value type", so long as you can come up with some operation that combines two maps to produce a new one which is somehow the combination of the two inputs.

如果两个映射中都没有键出现,这很简单.如果两个映射中存在相同的键,那么我们需要组合键映射到的两个值.嗯,我们不是刚刚描述了一个运算符,它结合了两个相同类型的实体吗?这就是为什么在 Scalaz 中 Map[K, V] 的半群存在当且仅当 V 的半群存在 - V 的半群用于组合分配给相同键的两个映射中的值.

If there are no keys that appear in both maps, this is trivial. If the same key exists in both maps, then we need to combine the two values that the key maps to. Hmm, haven't we just described an operator which combines two entities of the same type? This is why in Scalaz a semigroup for Map[K, V] exists if and only if a Semigroup for V exists - V's semigroup is used to combine the values from two maps which are assigned to the same key.

因此,因为 Int 是这里的值类型,所以碰撞"不存在.1 键是通过两个映射值的整数相加来解析的(就像 Int 的半群运算符所做的那样),因此是 100 + 9.如果值是字符串,冲突将导致两个映射值的字符串连接(同样,因为这是 String 的半群运算符所做的).

So because Int is the value type here, the "collision" on the 1 key is resolved by integer addition of the two mapped values (as that's what Int's semigroup operator does), hence 100 + 9. If the values had been Strings, a collision would have resulted in string concatenation of the two mapped values (again, because that's what the semigroup operator for String does).

(有趣的是,因为字符串连接不是可交换的 - 即 "a" + "b" != "b" + "a" - 结果的半群运算是也不是.所以 map1 |+| map2 在 String 情况下与 map2 |+| map1 不同,但在 Int 情况下则不同.)

(And interestingly, because string concatenation is not commutative - that is, "a" + "b" != "b" + "a" - the resulting semigroup operation isn't either. So map1 |+| map2 is different from map2 |+| map1 in the String case, but not in the Int case.)

这篇关于合并两个地图并对相同键的值求和的最佳方法?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆