把Scala的两个Map合并,合并的时候会遇到相同的键和不同的键,
- 对与相同的键,合并后的值是两个Map的值的和,
- 对于只存在于一个Map中的键保留其值不变,对于下面两个map的合并
scala> val m1 = Map(1->10, 2->4)
m1: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 4)
scala> val m2 = Map(2->5, 4->8)
m2: scala.collection.immutable.Map[Int,Int] = Map(2 -> 5, 4 -> 8)
想要的结果:
m3: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 9, 4 -> 8)
采用scala ++后的结果:
scala> m1 ++ m2
res0: scala.collection.immutable.Map[Int,Int] = Map(1 -> 10, 2 -> 5, 4 -> 8)
这里结果显然不符合预期,我们希望key=2的value是4+5=9,而这里等于5,说明在两个map merge时第二个map的key-value:(2,5)覆盖了第一个map的值(2,4).
1. foreach
此种方法最为简单,就不进行示例。
2. groupBy
scala> val list = m1.toList ++ m2.toList
//list: List[(Int, Int)] = List((1,10), (2,4), (2,5), (4,8))
scala> val merged = list
.groupBy ( _._1)
.map { case (k,v) => k -> v.map(_._2).sum }
//merged: scala.collection.immutable.Map[Int,Int] = Map(2 -> 9, 4 -> 8, 1 -> 10)
此方法先把Map转化成list,然后进行一个groupBy操作,把相同的key聚合到一起,之后进行一个求和。该方法有点是可以处理更多个Map的merge,但缺点也是显而易见的,Map转化为List又转回Map造成了一定开销,并且groupBy代价比较大。
3. foldLeft
scala> val merged = (m1 /: m2) {
case (map, (k,v)) =>
map + ( k -> (v + map.getOrElse(k, 0)) )
}
//merged: scala.collection.immutable.Map[Int,Int] =
//Map(1 -> 10, 2 -> 9, 4 -> 8)
这部分代码比较抽象,其中(m1 /: m2) 等价于 m2.foldLeft(m1),可以形象地理解为向左折叠。而foldLeft参数需要接收两个,另外一个参数列表使用case 匹配到一个结果map 和 当前m1中的一个(k,v) pair,这个pair就是foldLeft过程中遍历的每一个值。通过这些操作最终获得我们需要的结果。
4. scalaz
scalaz封装了一个更加优美的二元函数操作符|+|,直接操作两个Map就能得到结果。首先启动scala时附带加入scalaz的包。关于scalaz的说明和下载如下
- scalaz in Github
- scalaz Document
- download jar of scalaz 从这里已下载到scalaz的jar包之后就可以在启动scala的时候引入这个包
scala -cp scalaz-core_2.11-7.1.1.jar
在shell中导入scalaz.Scalaz._就可以使用它的隐式“|+|”操作符来merge我们的两个Map了,非常简洁。
scala> import scalaz.Scalaz._
import scalaz.Scalaz._
scala> m1 |+| m2
res5: scala.collection.immutable.Map[Int,Int] = Map(2 -> 9, 4 -> 8, 1 -> 10)
文章转载来源:http://wangjiachun.github.io/2017/06/26/scala-merge-two-Map/