【问题标题】:Scala nested map mergeScala嵌套地图合并
【发布时间】:2015-04-12 00:08:50
【问题描述】:

我想合并嵌套地图,但不知道如何合并内部地图。

var a = Map[String,Map[String,String]]()
a = a + ("key1" -> Map("subkey1" -> "a"))
a = a + ("key1" -> Map("subkey2" -> "b"))
a = a + ("key2" -> Map("subkey1" -> "c"))

我想合并所有这些以获得以下结果:

Map("key1" -> Map("subkey1" -> "a", "subkey2" -> "b"), "key2" -> Map("subkey1" -> "c"))

这有什么标准方法吗?

【问题讨论】:

  • 如果子映射中的key发生冲突怎么办?就像key1 映射下的两个subkey1
  • @m-z 理想情况下,碰撞可以由 2-arity 函数处理。在我的情况下,签名更像是 Map[String,Map[String,Seq[String]]],然后将这些值组合在一起。

标签: scala scala-collections


【解决方案1】:

如果可以使用 Scalaz - semigroups 可能会有所帮助:

import scalaz._, Scalaz._
val map1 = Map("key1" -> Map("subkey1" -> "a"))
val map2 = Map("key1" -> Map("subkey2" -> "b"))
val map3 = Map("key2" -> Map("subkey1" -> "c"))

scala> map1 |+| map2 |+| map3
res0: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]] = 
   Map(key2 -> Map(subkey1 -> c), key1 -> Map(subkey2 -> b, subkey1 -> a))

唯一的限制——你的值应该定义Semigroup以处理冲突:

trait A
object A1 extends A
object A2 extends A 

implicit val ASemigroup = new Semigroup[A] {
  def append(a: A, b: => A) : A = a //"choose first" strategy
}

val map1 = Map("key1" -> Map("subkey1" -> (A1: A)))
val map2 = Map("key1" -> Map("subkey1" -> (A2: A)))

scala> map1 |+| map2
res8: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,A]] = 
   Map(key1 -> Map(subkey1 -> A1$@2cb79bd1))

顺便说一句,字符串上已经定义了 Semigroup,所以碰撞会导致字符串在那里连接。

【讨论】:

    【解决方案2】:

    没有任何内置的东西可以很好地工作,但getOrElse 是你的朋友。逻辑的核心,如果你确定没有子图集合,看起来像

    val x = a.getOrElse(key, mutable.Map.empty[String,String])
    a = a + (key -> (x ++ subMap))
    

    如果你可能有冲突,你需要做一些除了++之外的事情——可能再次使用相同的技巧来获取子键并更新值(如果存在)。

    【讨论】:

      【解决方案3】:

      似乎没有为此提供直接方法。你可以提供一个辅助方法。

      def mergeUpdate[K1, K2, V](base: Map[K1, Map[K2, V]], tuple: (K1, Map[K2, V])) = {
        base + (tuple._1 -> (base.getOrElse(tuple._1, Map.empty) ++ tuple._2))
      }
      

      然后重写你的代码:

      var a = Map[String,Map[String,String]]()
      a = mergeUpdate(a, ("key1" -> Map("subkey1" -> "a")))
      a = mergeUpdate(a, ("key1" -> Map("subkey2" -> "b")))
      a = mergeUpdate(a, ("key2" -> Map("subkey1" -> "c")))
      
      // =>
      a: scala.collection.immutable.Map[String,Map[String,String]] = Map(key1 -> Map(subkey1 -> a, subkey2 -> b), key2 -> Map(subkey1 -> c))
      

      【讨论】:

        【解决方案4】:

        如果可以拉取其他依赖,可以试试这个

        scala> import com.daodecode.scalax.collection.extensions._
        import com.daodecode.scalax.collection.extensions._
        
        scala> val m1 = "key1" -> Map("subkey1" -> "a")
        m1: (String, scala.collection.immutable.Map[String,String]) = (key1,Map(subkey1 -> a))
        
        scala> val m2 = "key1" -> Map("subkey2" -> "b")
        m2: (String, scala.collection.immutable.Map[String,String]) = (key1,Map(subkey2 -> b))
        
        scala> val m3 = "key2" -> Map("subkey1" -> "c")
        m3: (String, scala.collection.immutable.Map[String,String]) = (key2,Map(subkey1 -> c))
        
        scala> Seq(m1, m2, m3).toCompleteMap.
          mapValues(_.foldLeft(Map.empty[String,String]){ 
            case (acc, m) => acc.mergedWith(m)(_ + _)})
        res0: scala.collection.immutable.Map[String,scala.collection.immutable.Map[String,String]] = Map(key2 -> Map(subkey1 -> c), key1 -> Map(subkey1 -> a, subkey2 -> b))
        

        toCompleteMapmergedWith 是来自 https://github.com/jozic/scalax-collection 的扩展方法。它已发布到 maven Central

        (_ + _)这里是你自己的冲突解决函数

        【讨论】:

          猜你喜欢
          • 2016-03-27
          • 2015-04-18
          • 1970-01-01
          • 2015-02-11
          • 1970-01-01
          • 2018-10-25
          • 2020-10-26
          • 2010-11-18
          • 2018-02-01
          相关资源
          最近更新 更多