mutable.Map 깊은 병합

Dec 01 2020

Scala에서 두 개의 변경 가능한 맵을 깊이 병합하는 간결한 방법이 있습니까?

case class K1(i: Int)
case class K2(i: Int)

def deepMerge(map: mutable.Map[K1, Map[K2, List[Int]]],
              mergee: mutable.Map[K1, Map[K2, List[Int]]]
): Unit = ???

예 :

나는.

val map = mutable.Map(K1(1) -> Map(K2(1) -> List(1)))
val mergee = mutable.Map(K1(1) -> Map(K2(1) -> List(2)))

deepMerge(map, mergee)
map = mutable.Map(K1(1) -> Map(K2(1) -> List(1, 2)))

II.

val map = mutable.Map(K1(1) -> Map(K2(1) -> List(1)))
val mergee = mutable.Map(K1(1) -> Map(K2(2) -> List(1)))

deepMerge(map, mergee)
map = mutable.Map(K1(1) -> Map(K2(1) -> List(1), K2(2) -> List(1)))

III.

val map = mutable.Map(K1(1) -> Map(K2(1) -> List(1)))
val mergee = mutable.Map(K1(2) -> Map(K2(2) -> List(1)))

deepMerge(map, mergee)
map = mutable.Map(K1(1) -> Map(K2(1) -> List(1)), K1(2) -> Map(K2(2) -> List(1)))

즉, 두 맵에 동일한 키가있는 경우 키에 해당하는 값 ( List[Int])이 병합됩니다.

특정 키가 다른 맵에 표시되는지 여부를 확인하지 않고 간결하게 구현하는 방법이 있습니까? scalaz 또는 cats와 같은 FP 라이브러리를 사용하는 것도 괜찮습니다.

답변

3 KrzysztofAtłasik Dec 01 2020 at 15:40

고양이를 사용하여 또 다른 답변을 추가하고 있습니다.

당신이 설명하는 것은 실제로 cats.Semigroup 의 행동입니다 . 따라서 결합 ( |+|) 연산자를 사용하여 맵을 심층 병합 할 수 있습니다.

import cats.implicits._
import cats._

case class K1(i: Int)
case class K2(i: Int)

val map = Map(K1(1) -> Map(K2(1) -> List(1)))
val mergee = Map(K1(1) -> Map(K2(1) -> List(2)))

val deepMerged = map |+| mergee

println(deepMerged) // HashMap(K1(1) -> HashMap(K2(1) -> List(1, 2)))

문제는 cats lib가에 대한 Semigroup 인스턴스를 제공하지 않지만 mutable.Map불변 의 인스턴스에서 파생 할 수 있다는 것입니다.

import cats.implicits._
import scala.collection.immutable
import scala.collection.mutable
import cats._

//here I derivive Semigroup instance for mutable.Map from instance for immutable.Map
implicit def mutableMapSemigroup[K, V: Semigroup]: Semigroup[mutable.Map[K, V]] = Semigroup[immutable.Map[K, V]].imap(c => mutable.Map.from(c))(c => immutable.Map.from(c))

case class K1(i: Int)
case class K2(i: Int)

val map = mutable.Map(K1(1) -> mutable.Map(K2(1) -> List(1)))
val mergee = mutable.Map(K1(1) -> mutable.Map(K2(1) -> List(2)))

println(map |+| mergee)

그러나 이것은 실제로 변경 가능한 맵을 불변으로 변환 한 다음 병합을 수행 한 다음 다시 변경 가능한 맵으로 변환하므로 아마도 그다지 효율적이지 않을 것입니다.

1 jwvh Dec 01 2020 at 14:29

이것은 할 수 있습니다.

def deepMerge(mergeA: Map[K1, Map[K2, List[Int]]],
              mergeB: Map[K1, Map[K2, List[Int]]]
             ): Map[K1,Map[K2,List[Int]]] =
  (mergeA.toList ++ mergeB.toList).groupMap(_._1)(_._2).map{
    case (k1,ms) =>
      k1 -> ms.flatMap(_.toList).groupMap(_._1)(_._2).map{
        case (k2,ls) => k2 -> ls.flatten
      }
    }

mutable지도로 테스트 하지는 않았지만 거의 동일하게 작동합니다.