斯卡拉:如何合并的地图集合

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了斯卡拉:如何合并的地图集合相关的知识,希望对你有一定的参考价值。

我有地图[字符串,双]的名单,我想他们的内容合并到一个单一的地图[字符串,双]。我应该怎么做这个以惯用的方式是什么?我想,我应该能够折叠做到这一点。就像是:

val newMap = Map[String, Double]() /: listOfMaps { (accumulator, m) => ... }

此外,我想在一个通用的方式来处理关键的碰撞。也就是说,如果我添加一键已经存在的地图,我应该能够指定一个返回Double(在这种情况下),并采取现有的值该键的功能,再加上我试图添加值。如果钥匙还没有在地图上存在,则只需添加它和它的价值不变。

在我的特定情况下,我想建立一个单一的地图[字符串,双]例如,如果地图已经包含了一个键,然后双击将被添加到现有的映射值。

我在我的具体代码可变地图的工作,但我感兴趣的是更通用的解决方案,如果可能的话。

答案

这个怎么样:

def mergeMap[A, B](ms: List[Map[A, B]])(f: (B, B) => B): Map[A, B] =
  (Map[A, B]() /: (for (m <- ms; kv <- m) yield kv)) { (a, kv) =>
    a + (if (a.contains(kv._1)) kv._1 -> f(a(kv._1), kv._2) else kv)
  }

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
val mm = mergeMap(ms)((v1, v2) => v1 + v2)

println(mm) // prints Map(hello -> 5.5, world -> 2.2, goodbye -> 3.3)

它工作在两个2.7.5和2.8.0。

另一答案

嗯,你可以这样做:

mapList reduce (_ ++ _)

除了碰撞的特殊要求。

既然你有特殊的要求,也许最好的是做这样的事情(2.8):

def combine(m1: Map, m2: Map): Map = {
  val k1 = Set(m1.keysIterator.toList: _*)
  val k2 = Set(m2.keysIterator.toList: _*)
  val intersection = k1 & k2

  val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
  val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_)) 
  r2 ++ r1
}

然后,您可以将这个方法添加到通过皮条客我的图书馆模式的地图类,并在原来的例子,而不是“++”使用它:

class CombiningMap(m1: Map[Symbol, Double]) {
  def combine(m2: Map[Symbol, Double]) = {
    val k1 = Set(m1.keysIterator.toList: _*)
    val k2 = Set(m2.keysIterator.toList: _*)
    val intersection = k1 & k2
    val r1 = for(key <- intersection) yield (key -> (m1(key) + m2(key)))
    val r2 = m1.filterKeys(!intersection.contains(_)) ++ m2.filterKeys(!intersection.contains(_))
    r2 ++ r1
  }
}

// Then use this:
implicit def toCombining(m: Map[Symbol, Double]) = new CombiningMap(m)

// And finish with:
mapList reduce (_ combine _)

虽然这是写在2.8,所以keysIterator成为keys 2.7,filterKeys可能需要filtermap的方面来写,&变得**,等等,它应该不会太不同。

另一答案

我很惊讶,没有人想出这个解决方案尚未:

myListOfMaps.flatten.toMap

究竟你需要的东西:

  1. 合并列表到单个地图
  2. 杂草出任何重复键

例:

scala> List(Map('a -> 1), Map('b -> 2), Map('c -> 3), Map('a -> 4, 'b -> 5)).flatten.toMap
res7: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 4, 'b -> 5, 'c -> 3)

flatten变成地图到元组的平面列表清单,toMap轮流元组的名单与所有的重复键取出地图

另一答案

我赶紧阅读这个问题,所以我不知道如果我失去了一些东西(像它为2.7.x或没有scalaz工作):

import scalaz._
import Scalaz._
val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms.reduceLeft(_ |+| _)
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2)

您可以更改独异定义为双并获得另一种方式来累积值,及彼最大:

implicit val dbsg: Semigroup[Double] = semigroup((a,b) => math.max(a,b))
ms.reduceLeft(_ |+| _)
// returns Map(goodbye -> 3.3, hello -> 4.4, world -> 2.2)
另一答案

有趣的是,这个有点绕noodling,我得到了以下(上2.7.5):

一般地图:

   def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: Seq[scala.collection.Map[A,B]]): Map[A, B] = {
    listOfMaps.foldLeft(Map[A, B]()) { (m, s) =>
      Map(
        s.projection.map { pair =>
        if (m contains pair._1)
          (pair._1, collisionFunc(m(pair._1), pair._2))
        else
          pair
      }.force.toList:_*)
    }
  }

但是男人,那是可怕的与投影和强制和toList和诸如此类的东西。另外一个问题:什么是更好的方式来处理这种内折?

对于易变的地图,这是我在我的代码处理,并用较少的通用的解决方案,我得到这个:

def mergeMaps[A,B](collisionFunc: (B,B) => B)(listOfMaps: List[mutable.Map[A,B]]): mutable.Map[A, B] = {
    listOfMaps.foldLeft(mutable.Map[A,B]()) {
      (m, s) =>
      for (k <- s.keys) {
        if (m contains k)
          m(k) = collisionFunc(m(k), s(k))
        else
          m(k) = s(k)
      }
      m
    }
  }

这似乎有点清洁,但因为它是书面只能与可变地图工作。有趣的是,我第一次尝试上面使用/(之前我问的问题):不是foldLeft,但我得到的类型错误。我想/:和foldLeft基本上是相等的,但是编译器不停地抱怨,我需要明确的类型(M,S)。那是怎么回事?

另一答案

我写了一篇博客文章中针对此,检查出来:

http://www.nimrodstech.com/scala-map-merge/

基本上都采用半scalaz组,你可以很容易地做到这一点

看起来是这样的:

  import scalaz.Scalaz._
  listOfMaps reduce(_ |+| _)
另一答案

开始Scala 2.13,它处理重复的键,并且仅基于标准库的另一解决方案应用新Map算子(顾名思义)是flatten的等效后跟一个映射之前在于合并groupMapReduces如序列(groupBy)以及降低分组值的步骤:

List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
  .flatten
  .groupMapReduce(_._1)(_._2)(_ + _)
// Map("world" -> 2.2, "goodbye" -> 3.3, "hello" -> 5.5)

这个:

  • flattens(符连接)的地图作为元组序列(List(("hello", 1.1), ("world", 2.2), ("goodbye", 3.3), ("hello", 4.4))),这使所有键/值(即使重复键)
  • 基于它们的第一元组部分(group)(groupMapReduce的组份)_._1s元件
  • maps分组值,以它们的第二元组部分(_._2)(groupMapReduce的地图部分)
  • reduces映射分组的值(_+_)通过取它们的总和(但它可以是任何reduce: (T, T) => T功能)(减少groupMapReduce的一部分)

所述groupMapReduce步骤可以被看作是一个one-pass version等效的:

list.groupBy(_._1).mapValues(_.map(_._2).reduce(_ + _))
另一答案

一个oneliner帮手,FUNC,其使用读取几乎一样使用scalaz清洁:

def mergeMaps[K,V](m1: Map[K,V], m2: Map[K,V])(f: (V,V) => V): Map[K,V] =
    (m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) })

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms.reduceLeft(mergeMaps(_,_)(_ + _))
// returns Map(goodbye -> 3.3, hello -> 5.5, world -> 2.2)

对于最终的可读性包装在一个隐含的定制类型:

class MyMap[K,V](m1: Map[K,V]) {
    def merge(m2: Map[K,V])(f: (V,V) => V) =
    (m1 -- m2.keySet) ++ (m2 -- m1.keySet) ++ (for (k <- m1.keySet & m2.keySet) yield { k -> f(m1(k), m2(k)) })
}
implicit def toMyMap[K,V](m: Map[K,V]) = new MyMap(m)

val ms = List(Map("hello" -> 1.1, "world" -> 2.2), Map("goodbye" -> 3.3, "hello" -> 4.4))
ms reduceLeft { _.merge(_)(_ + _) } 

以上是关于斯卡拉:如何合并的地图集合的主要内容,如果未能解决你的问题,请参考以下文章

Scala:如何合并地图集合

如何从片段外部清除/重置地图?

如何更改谷歌地图标记上方的标题和片段设计

如何在滚动视图中设置谷歌地图片段

如何在地图片段 API v2 布局顶部添加按钮

如何在用java创建的布局内创建地图片段(GoogleMap)?