Scala: карта слияния
Как слить карты, как показано ниже:
Map1 = Map(1 -> Class1(1), 2 -> Class1(2))
Map2 = Map(2 -> Class2(1), 3 -> Class2(2))
После объединения.
Merged = Map( 1 -> List(Class1(1)), 2 -> List(Class1(2), Class2(1)), 3 -> Class2(2))
Может быть List, Set или любой другой коллекцией, у которой есть атрибут размера.
Ответы
Ответ 1
Используя стандартную библиотеку lib, вы можете сделать это следующим образом:
// convert maps to seq, to keep duplicate keys and concat
val merged = Map(1 -> 2).toSeq ++ Map(1 -> 4).toSeq
// merged: Seq[(Int, Int)] = ArrayBuffer((1,2), (1,4))
// group by key
val grouped = merged.groupBy(_._1)
// grouped: scala.collection.immutable.Map[Int,Seq[(Int, Int)]] = Map(1 -> ArrayBuffer((1,2), (1,4)))
// remove key from value set and convert to list
val cleaned = grouped.mapValues(_.map(_._2).toList)
// cleaned: scala.collection.immutable.Map[Int,List[Int]] = Map(1 -> List(2, 4))
Ответ 2
Это простейшая реализация, с которой я мог бы прийти,
val m1 = Map(1 -> "1", 2 -> "2")
val m2 = Map(2 -> "21", 3 -> "3")
def merge[K, V](m1:Map[K, V], m2:Map[K, V]):Map[K, List[V]] =
(m1.keySet ++ m2.keySet) map { i => i -> (m1.get(i).toList ::: m2.get(i).toList) } toMap
merge(m1, m2) // Map(1 -> List(1), 2 -> List(2, 21), 3 -> List(3))
Ответ 3
Вы можете использовать scalaz:
import scalaz._, Scalaz._
val m1 = Map('a -> 1, 'b -> 2)
val m2 = Map('b -> 3, 'c -> 4)
m1.mapValues{List(_)} |+| m2.mapValues{List(_)}
// Map('b -> List(2, 3), 'c -> List(4), 'a -> List(1))
Вы можете использовать Set(_)
вместо List(_)
, чтобы получить Set
как значения в Map
.
См. Полугруппа в чит-лист scalaz (или обучающий скалаз) для получения более подробной информации о |+|
.
Для Int
|+|
работает как +
, для List
- как ++
, для Map
он применяет |+|
к значениям тех же ключей.
Ответ 4
Один чистый способ сделать это с кошками:
import cats.implicits._
Map(1 -> "Hello").combine(Map(2 -> "Goodbye"))
//Map(2 -> Goodbye, 1 -> Hello)
Важно отметить, что обе карты должны быть одного типа (в данном случае, Map[Int, String]
).
Длинное объяснение:
combine
самом деле не является членом карты. Импортируя cats.implicits, вы вводите в область видимости cat встроенные моноидные экземпляры cat, а также некоторые неявные классы, которые включают краткий синтаксис.
Вышеуказанное эквивалентно этому:
Monoid[Map[Int, String]].combine(Map(1 -> "Hello"), Map(2 -> "Goodbye"))
Там, где мы используем функцию "summoner" Monoid, чтобы получить экземпляр Monoid [Map [Int, String]] в области видимости и использовать его функцию объединения.
Ответ 5
Я написал сообщение в блоге об этом, проверьте:
http://www.nimrodstech.com/scala-map-merge/
в основном с использованием полугруппы scalaz вы можете достичь этого довольно легко
будет выглядеть примерно так:
import scalaz.Scalaz._
Map1 |+| Map2
Ответ 6
Если вы не хотите возиться с оригинальными картами, вы можете сделать что-то вроде следующего
val target = map1.clone()
val source = map2.clone()
source.foreach(e => target += e._1 -> e._2)
Ответ 7
left.keys map { k => k -> List(left(k),right(k)) } toMap
Является кратким и будет работать, если ваши две карты left
и right
. Не уверен в эффективности.
Но ваш вопрос немного неоднозначен по двум причинам. Вы не указываете
- Отношение подтипирования между значениями (т.е.
class1
, class2
),
- Что произойдет, если карты имеют разные ключи.
В первом случае рассмотрим следующий пример:
val left = Map("foo" ->1, "bar" ->2)
val right = Map("bar" -> 'a', "foo" -> 'b')
В результате получается
res0: Map[String,List[Int]] = Map(foo -> List(1, 98), bar -> List(2, 97))
Обратите внимание, что Char
были преобразованы в Int
s из-за иерархии типов scala. В более общем плане, если в вашем примере class1
и class2
не связаны, вы вернетесь к List[Any]
; это, вероятно, не то, что вы хотели.
Вы можете обойти это, отбросив конструктор List
из моего ответа; это вернет Tuple
, которые сохраняют тип:
res0: Map[String,(Int, Char)] = Map(foo -> (1,b), bar -> (2,a))
Вторая проблема - это то, что происходит, когда у вас есть карты, у которых нет одинаковых ключей. Это приведет к исключению key not found
. Иными словами, вы делаете левое, правое или внутреннее соединение двух карт? Вы можете устранить тип соединения, переключившись на right.keys
или right.keySet ++ left.keySet
для правого/внутреннего соединений соответственно. Позже будет работать проблема с отсутствующим ключом, но, возможно, это не то, что вы хотите, может быть, вам нужно вместо этого использовать левое или правое соединение. В этом случае вы можете использовать метод withDefault
Map
, чтобы каждый ключ возвращал значение, например. None
, но для этого требуется немного больше работы.
Ответ 8
Вы можете использовать foldLeft для объединения двух Карт того же типа
def merge[A, B](a: Map[A, B], b: Map[A, B])(mergef: (B, Option[B]) => B): Map[A, B] = {
val (big, small) = if (a.size > b.size) (a, b) else (b, a)
small.foldLeft(big) { case (z, (k, v)) => z + (k -> mergef(v, z.get(k))) }
}
def mergeIntSum[A](a: Map[A, Int], b: Map[A, Int]): Map[A, Int] =
merge(a, b)((v1, v2) => v2.map(_ + v1).getOrElse(v1))
Пример:
val a = Map("a" -> 1, "b" -> 5, "c" -> 6)
val b = Map("a" -> 4, "z" -> 8)
mergeIntSum(a, b)
res0: Map[String,Int] = Map(a -> 5, b -> 5, c -> 6, z -> 8)
Ответ 9
m2.foldLeft(m1.mapValues{List[CommonType](_)}) { case (acc, (k, v)) =>
acc.updated(k, acc.getOrElse(k, List.empty) :+ v)
}
Как отмечено jwvh, тип списка должен быть указан явно, если Class1 не относится к верхнему типу для класса 2. CommonType - это тип, который является верхней границей для Class1 и Class2.
Ответ 10
Начиная с Scala 2.13
, другое решение, основанное только на стандартной библиотеке, состоит в использовании K)(f:A=>B):scala.collection.immutable.Map[K,CC[B]] rel="nofollow noreferrer">groupMap, который (как следует из его названия) является эквивалентом groupBy
за которым следует mapValues
:
// val m1 = Map(1 -> "a", 2 -> "b")
// val m2 = Map(2 -> "c", 3 -> "d")
(m1.toSeq ++ m2.toSeq).groupMap(_._1)(_._2)
// Map[Int,Seq[String]] = Map(2 -> List("b", "c"), 1 -> List("a"), 3 -> List("d"))
Это:
-
объединяет две карты в виде последовательности кортежей (List((1,"a"), (2,"b"), (2,"c"), (3,"d"))
)
-
group
элементы на основе их первой части кортежа (групповая часть группы Map)
-
map
сгруппированные значения с их второй частью кортежа (часть карты группы Map)