最好的方式来合并两个地图,并且总和相同的键的值? [英] Best way to merge two maps and sum the values of same key?

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

问题描述

$ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $ $%

我想合并它们,并且总计相同的键的值。所以结果将是:

 映射(2-> 20,1-> 109,3-> 300) 

现在我有两个解决方案:

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

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

解决方案

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

  scala> import scalaz._ 
import scalaz._

scala>进口Scalaz._
import Scalaz._

scala> val map1 =映射(1→9,2→20)
map1:scala.collection.immutable.Map [Int,Int] =映射(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 的标准半群使用加法运算符,所以您得到每个重复键的值的总和。



编辑:根据用户482745的要求,详细一点。



数学上一个 semigroup 只是一组值,以及从该集合中获取两个值的运算符,并从该集合中生成另一个值。因此,加法下的整数是一个半群,例如 - + 运算符组合两个int来创建另一个int。



您还可以在所有映射与给定键类型和值类型的集合之间定义半群,只要您可以提出一些组合两个映射来生成新映射的操作,这些操作以某种方式组合在一起输入。



如果没有两个地图中出现的键,这是微不足道的。如果两个地图中都存在相同的密钥,那么我们需要组合键映射到的两个值。嗯,我们还没有描述一个组合两个相同类型的实体的运算符?这就是为什么在Scalaz为映射[K,V] 的半群,当且仅当存在 V 的Semigroup - V 的semigroup用于组合分配给同一个键的两个地图中的值。



因此,因为 Int 是这里的值类型, 1 键上的碰撞是通过整数加法来解决的两个映射的值(这就是Int的半群算子),因此 100 + 9 。如果这些值是Strings,则会导致两个映射值的字符串连接(再次,因为这是String的半组运算符)。



有趣的是,因为字符串连接不是可交换的 - 也就是说,a+b!=b+a - 生成的半组操作不是所以 map1 | + | map2 与String中的 map2 | + | map1 不同,但不在Int案例。)


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 }

and

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

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.

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

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.

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.

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

(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天全站免登陆