Скажем, у меня есть две карты:
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
Я хочу объединить эти карты по ключу, применяя некоторую функцию для сбора значений (в этом конкретном случае я хочу собрать их в seq, давая:
val c = Map(1 -> Seq("one", "un"), 2 -> Seq("two", "deux"), 3 -> Seq("three", "trois"))
Такое ощущение, что должен быть хороший, идиоматический способ сделать это.
val a = Map(1 -> "one", 2 -> "two", 3 -> "three")
val b = Map(1 -> "un", 2 -> "deux", 3 -> "trois")
val c = a.toList ++ b.toList
val d = c.groupBy(_._1).map{case(k, v) => k -> v.map(_._2).toSeq}
//res0: scala.collection.immutable.Map[Int,Seq[java.lang.String]] =
//Map((2,List(two, deux)), (1,List(one, un), (3,List(three, trois)))
Так что я не был доволен ни одним из этих решений (я хочу создать новый тип, поэтому полугруппа на самом деле не чувствует себя уместно, а решение Infinity казалось довольно сложным), так что на данный момент я пошел с этим. Я был бы рад видеть, что это улучшилось:
def merge[A,B,C](a : Map[A,B], b : Map[A,B])(c : (B,B) => C) = {
for (
key <- (a.keySet ++ b.keySet);
aval <- a.get(key); bval <- b.get(key)
) yield c(aval, bval)
}
merge(a,b){Seq(_,_)}
Я хотел, чтобы поведение ничего не возвращало, когда ключ не присутствовал ни на одной карте (что отличается от других решений), но был способ указать это было бы хорошо.
def merge[A,B,C,D](b : Map[A,B], c : Map[A,C])(d : (Option[B],Option[C]) => D): Map[A,D] = {
(b.keySet ++ c.keySet).map(k => k -> d(b.get(k), c.get(k))).toMap
}
def optionSeqBiFunctionK[A]:(Option[A], Option[A]) => Seq[A] = _.toSeq ++ _.toSeq
merge(a,b)(optionSeqBiFunctionK)