Лучший способ объединить две карты и суммировать значения одного и того же ключа? - PullRequest
162 голосов
/ 16 августа 2011
val map1 = Map(1 -> 9 , 2 -> 20)
val map2 = Map(1 -> 100, 3 -> 300)

Я хочу объединить их и суммировать значения тех же ключей.Таким образом, результат будет:

Map(2->20, 1->109, 3->300)

Теперь у меня есть 2 решения:

val list = map1.toList ++ map2.toList
val merged = list.groupBy ( _._1) .map { case (k,v) => k -> v.map(_._2).sum }

и

val merged = (map1 /: map2) { case (map, (k,v)) =>
    map + ( k -> (v + map.getOrElse(k, 0)) )
}

Но я хочу знать, есть ли какие-нибудь лучшерешения.

Ответы [ 14 ]

2 голосов
/ 08 августа 2016

Ответ Анджея Дойла содержит отличное объяснение полугрупп, которое позволяет использовать оператор |+| для объединения двух карт и суммирования значений для совпадающих ключей.

Существует множество способов определения чего-либоэкземпляр класса типов, и, в отличие от OP, вы, возможно, не захотите специально суммировать свои ключи.Или, возможно, вы захотите работать на объединении, а не на пересечении.Scalaz также добавляет дополнительные функции к Map для этой цели:

https://oss.sonatype.org/service/local/repositories/snapshots/archive/org/scalaz/scalaz_2.11/7.3.0-SNAPSHOT/scalaz_2.11-7.3.0-SNAPSHOT-javadoc.jar/!/index.html#scalaz.std.MapFunctions

Вы можете сделать

import scalaz.Scalaz._

map1 |+| map2 // As per other answers
map1.intersectWith(map2)(_ + _) // Do things other than sum the values
1 голос
/ 18 апреля 2019

Вот что я в итоге использовал:

(a.toSeq ++ b.toSeq).groupBy(_._1).mapValues(_.map(_._2).sum)
1 голос
/ 23 ноября 2015

Это то, что я придумал ...

def mergeMap(m1: Map[Char, Int],  m2: Map[Char, Int]): Map[Char, Int] = {
   var map : Map[Char, Int] = Map[Char, Int]() ++ m1
   for(p <- m2) {
      map = map + (p._1 -> (p._2 + map.getOrElse(p._1,0)))
   }
   map
}
0 голосов
/ 16 декабря 2014

У меня есть небольшая функция для работы, она находится в моей маленькой библиотеке для некоторых часто используемых функций, которых нет в стандартной библиотеке. Он должен работать для всех типов карт, изменяемых и неизменяемых, не только HashMaps

Вот использование

scala> import com.daodecode.scalax.collection.extensions._
scala> val merged = Map("1" -> 1, "2" -> 2).mergedWith(Map("1" -> 1, "2" -> 2))(_ + _)
merged: scala.collection.immutable.Map[String,Int] = Map(1 -> 2, 2 -> 4)

https://github.com/jozic/scalax-collection/blob/master/README.md#mergedwith

А вот и тело

def mergedWith(another: Map[K, V])(f: (V, V) => V): Repr =
  if (another.isEmpty) mapLike.asInstanceOf[Repr]
  else {
    val mapBuilder = new mutable.MapBuilder[K, V, Repr](mapLike.asInstanceOf[Repr])
    another.foreach { case (k, v) =>
      mapLike.get(k) match {
        case Some(ev) => mapBuilder += k -> f(ev, v)
        case _ => mapBuilder += k -> v
      }
    }
    mapBuilder.result()
  }

https://github.com/jozic/scalax-collection/blob/master/src%2Fmain%2Fscala%2Fcom%2Fdaodecode%2Fscalax%2Fcollection%2Fextensions%2Fpackage.scala#L190

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...