如何在Scala中添加内部地图的元组值

时间:2018-07-31 17:36:38

标签: scala

我有一个场景,我有2张地图,并且需要获取内部地图值的总和

val map1 = Map("String" -> Map(LocalDate1 -> (10,10),LocalDate2 -> (20,20),LocalDate3 -> (30,30)))
val map2 = Map("String" -> Map(LocalDate1 -> (10,10),LocalDate2 -> (20,20),LocalDate3 -> (30,30)))

最终输出应如下所示

Map("String" -> Map(LocalDate1 -> (20,20),LocalDate2 -> (40,40),LocalDate3 -> (60,60)))

2 个答案:

答案 0 :(得分:0)

问题的确与链接的重复项非常相似,但是我认为由于希望合并元素而不是将所有元素都添加到列表中而使它有微妙的不同-这就是Semigroup类型类的确切用法。使用cats

import cats.implicits._
map1.combine(map2)

我敢肯定,Scalaz也可以做类似的事情。

但这不是很有启发性,因此本质上是幕后发生的事情-有一个Semigroup类型类和相关的语法糖隐式类:

trait Semigroup[A] {
  def combine(a1: A, a2: A): A
}

implicit class SemigroupOps[A : Semigroup](a1: A) {
  def combine(a2: A): A = implicitly[Semigroup[A]].combine(a1, a2)
}

我们当然知道如何添加整数:

implicit object IntAdder extends Semigroup[Int] {
  def combine(x: Int, y: Int): Int = x + y
}

如果您知道如何添加两个A和如何添加两个B,那么很容易添加两个(A, B)

implicit def pairSemigroup[A : Semigroup, B : Semigroup]: Semigroup[(A, B)] = new Semigroup[(A, B)] {
  def combine(x: (A, B), y: (A, B)): (A, B) = {
    (implicitly[Semigroup[A]].combine(x._1, y._1), implicitly[Semigroup[B]].combine(x._2, y._2))
  }
}

最后,如果您知道如何添加A,则可以通过合并和组合任何重叠的值来添加两个包含A作为值的映射。

implicit def mapSemigroup[K, V : Semigroup]: Semigroup[Map[K, V]] = new Semigroup[Map[K, V]] {
  def combine(x: Map[K, V], y: Map[K, V]): Map[K, V] = {
    val keys = x.keySet ++ y.keySet
    keys.map(k => (x.get(k), y.get(k)) match {
      case (Some(v), None) => k -> v
      case (None, Some(v)) => k -> v
      case (Some(v1), Some(v2)) => k -> implicitly[Semigroup[V]].combine(v1, v2)
    }).toMap
  }
}

现在在您的特定实例中,您有一个Map[String, Map[LocalDate, (Int, Int)]]。 scala编译器会为该类型寻找Semigroup,并发现mapSemigroup可能有效,如果它可以为Semigroup找到Map[LocalDate, (Int, Int)]。如果mapSemigroup可以为Semigroup找到一个(Int, Int),那么它可以再次工作。然后,如果pairSemigroup可以为Semigroup(和再次Int)找到Int,则可能会起作用。它直接具有,因此构造并使用了顶级类型的隐式Semigroup

答案 1 :(得分:0)

此功能可以满足您的要求:

 def mergeMaps(map1:Map[String,Map[String,(Int,Int)]],map2:Map[String,Map[String,(Int,Int)]]) = {
       def addTups(a:(Int,Int),b:(Int,Int)) = (a._1+b._1,a._2+b._2)
       val res = map1.map(x=>{val m = map2.getOrElse(x._1,Map());
          (x._1,x._2.map(y=>(y._1,addTups(y._2,m.getOrElse(y._1,(0,0))))))})
       val map2Extras = map2.filter(x=>map1.keys.forall(_!=x._1))
       res ++ map2Extras
  }

在Scala REPL中:

scala> val map1 = Map("String" -> Map("LocalDate1" -> (10,10),"LocalDate2" -> (20,20),"LocalDate3" -> (30,30)))
map1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDate
1 -> (10,10), LocalDate2 -> (20,20), LocalDate3 -> (30,30)))

scala> val map2 = Map("String" -> Map("LocalDate1" -> (10,10),"LocalDate2" -> (20,20),"LocalDate3" -> (30,30)))
map2: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDate
1 -> (10,10), LocalDate2 -> (20,20), LocalDate3 -> (30,30)))

scala> mergeMaps(map1,map2)
res73: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDat
e1 -> (20,20), LocalDate2 -> (40,40), LocalDate3 -> (60,60)))

scala> val mapx2 = Map("String" -> Map("LocalDate1" -> (10,10), "LocalDate2" -> (20,20), "LocalDate3" -> (30,30)), "strExtra
map2" -> Map("ldatemap2" -> (90,90)))
mapx2: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDat
e1 -> (10,10), LocalDate2 -> (20,20), LocalDate3 -> (30,30)), strExtramap2 -> Map(ldatemap2 -> (90,90)))

scala> val mapx1 = Map("String" -> Map("LocalDate1" -> (20,20), "LocalDate2" -> (40,40), "LocalDate3" -> (60,60)), "strExtra
map1" -> Map("ldate" -> (23,21)))
mapx1: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDat
e1 -> (20,20), LocalDate2 -> (40,40), LocalDate3 -> (60,60)), strExtramap1 -> Map(ldate -> (23,21)))

scala> mergeMaps(mapx1,mapx2)
res100: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,(Int, Int)]] = Map(String -> Map(LocalDa
te1 -> (30,30), LocalDate2 -> (60,60), LocalDate3 -> (90,90)), strExtramap1 -> Map(ldate -> (23,21)), strExtramap2 -> Map(ld
atemap2 -> (90,90)))