val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
Run Code Online (Sandbox Code Playgroud)
我想合并它们,并将相同键的值相加.结果将是:
Map(2->20, 1->109, 3->300)
Run Code Online (Sandbox Code Playgroud)
现在我有2个解决方案:
val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }
Run Code Online (Sandbox Code Playgroud)
和
val merged = (map1 /: map2) { case (map, (k,v)) =>
map + ( k -> (v + map.getOrElse(k, 0)) )
}
Run Code Online (Sandbox Code Playgroud)
但我想知道是否有更好的解决方案.
Rex*_*err 146
我所知道的最简单的答案只使用标准库
map1 ++ map2.map{ case (k,v) => k -> (v + map1.getOrElse(k,0)) }
Run Code Online (Sandbox Code Playgroud)
And*_*yle 141
Scalaz有一个概念半群捕获你想在这里做什么,并导致无疑是最短/干净的解决方案:
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)
Run Code Online (Sandbox Code Playgroud)
具体来说,二元运算符用于Map[K, V]组合映射的键,折叠V的半群运算符对任何重复值.标准半群Int使用加法运算符,因此您可以获得每个重复键的值的总和.
编辑:根据user482745的要求更多细节.
在数学上,半群只是一组值,以及从该集合中获取两个值的运算符,并从该集合中生成另一个值.因此,例如,加法下的整数是半群 - +运算符将两个整数组合成另一个整数.
您还可以在"具有给定键类型和值类型的所有地图"的集合上定义半群,只要您能够提出一些组合两个地图以生成新地图的操作,这两个地图就是两者的组合.投入.
如果两个地图中都没有出现任何键,则这是微不足道的.如果两个映射中都存在相同的键,那么我们需要组合键映射到的两个值.嗯,我们还没有描述一个结合了两个相同类型实体的运算符吗?这就是为什么在Scalaz中Map[K, V]存在一个半群存在,当且仅当V存在半群时 - V半群用于组合分配给同一个键的两个映射的值.
因此,因为Int这里是值类型,所以1键上的"碰撞" 通过两个映射值的整数相加来解析(因为这是Int的半群运算符所做的),因此100 + 9.如果值是字符串,则碰撞会导致两个映射值的字符串连接(同样,因为这是String的半群运算符所做的).
(有趣的是,因为字符串连接不是可交换的 - 也就是说,"a" + "b" != "b" + "a"所得到的半群操作也不是.所以map1 |+| map2不同于map2 |+| map1String情况,但不是在Int情况下.)
Mat*_*ell 47
快速解决方案
(map1.keySet ++ map2.keySet).map {i=> (i,map1.getOrElse(i,0) + map2.getOrElse(i,0))}.toMap
Run Code Online (Sandbox Code Playgroud)
Mik*_*sov 39
好吧,现在在scala库中(至少在2.10中)有你想要的东西 - 合并功能.但它仅在HashMap中呈现,而不在Map中.这有点令人困惑.签名也很麻烦 - 无法想象为什么我需要两次密钥以及当我需要用另一把密钥生成一对时.但是,它比以前的"原生"解决方案更有效,更清洁.
val map1 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
val map2 = collection.immutable.HashMap(1 -> 11 , 2 -> 12)
map1.merged(map2)({ case ((k,v1),(_,v2)) => (k,v1+v2) })
Run Code Online (Sandbox Code Playgroud)
同样在scaladoc中提到过
该
merged方法平均比执行遍历和从头开始重建新的不可变哈希映射更高效,或者++.
Jeg*_*gan 13
这可以用简单的Scala 实现为Monoid.这是一个示例实现.通过这种方法,我们不仅可以合并2,还可以合并地图列表.
// Monoid trait
trait Monoid[M] {
def zero: M
def op(a: M, b: M): M
}
Run Code Online (Sandbox Code Playgroud)
Monoid特征的基于Map的实现,它合并了两个映射.
val mapMonoid = new Monoid[Map[Int, Int]] {
override def zero: Map[Int, Int] = Map()
override def op(a: Map[Int, Int], b: Map[Int, Int]): Map[Int, Int] =
(a.keySet ++ b.keySet) map { k =>
(k, a.getOrElse(k, 0) + b.getOrElse(k, 0))
} toMap
}
Run Code Online (Sandbox Code Playgroud)
现在,如果您有一个需要合并的地图列表(在这种情况下,只有2),可以像下面这样完成.
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
val maps = List(map1, map2) // The list can have more maps.
val merged = maps.foldLeft(mapMonoid.zero)(mapMonoid.op)
Run Code Online (Sandbox Code Playgroud)
map1 ++ ( for ( (k,v) <- map2 ) yield ( k -> ( v + map1.getOrElse(k,0) ) ) )
Run Code Online (Sandbox Code Playgroud)
我写了一篇关于此的博客文章,请查看:
http://www.nimrodstech.com/scala-map-merge/
基本上使用scalaz semi group你可以很容易地实现这一点
看起来像是这样的:
import scalaz.Scalaz._
map1 |+| map2
Run Code Online (Sandbox Code Playgroud)
您也可以使用Cats做到这一点。
import cats.implicits._
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)
map1 combine map2 // Map(2 -> 20, 1 -> 109, 3 -> 300)
Run Code Online (Sandbox Code Playgroud)
从 开始Scala 2.13,另一个仅基于标准库的解决方案包括替换groupBy解决方案的一部分,其中groupMapReduce(顾名思义)相当于groupBy后跟mapValues和减少步骤:
// val map1 = Map(1 -> 9, 2 -> 20)
// val map2 = Map(1 -> 100, 3 -> 300)
(map1.toSeq ++ map2).groupMapReduce(_._1)(_._2)(_+_)
// Map[Int,Int] = Map(2 -> 20, 1 -> 109, 3 -> 300)
Run Code Online (Sandbox Code Playgroud)
这:
将两个映射连接为元组序列 ( List((1,9), (2,20), (1,100), (3,300)))。为了简洁起见,map2被隐式转换为Seq以适应 - 的类型map1.toSeq,但您可以选择使用 使其显式map2.toSeq,
groups 元素基于其第一个元组部分( MapReduce组的组部分),
map将值分组到其第二元组部分(MapReduce组的映射部分),
reduce_+_通过求和来映射映射值 ( )(减少 groupMap Reduce的一部分)。