veränderlich.Map Deep Merge

Dec 01 2020

Gibt es eine präzise Möglichkeit, zwei veränderbare Karten in Scala tief zusammenzuführen?

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 = ???

Beispiele:

ICH.

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)))

Das heißt, wenn in beiden Karten derselbe Schlüssel dargestellt ist, werden die Werte, denen die Schlüssel entsprechen ( List[Int]), zusammengeführt.

Gibt es eine Möglichkeit, es präzise zu implementieren, ohne zu prüfen, ob der jeweilige Schlüssel in einer anderen Karte angezeigt wird oder nicht? Die Verwendung von FP-Bibliotheken wie Scalaz oder Katzen ist ebenfalls in Ordnung.

Antworten

3 KrzysztofAtłasik Dec 01 2020 at 15:40

Ich füge eine weitere Antwort mit Katzen hinzu.

Was Sie beschreiben, ist eigentlich das Verhalten von Katzen . Sie können also einfach den |+|Operator comb ( ) verwenden, um Karten tief zusammenzuführen:

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)))

Das Problem ist, dass cat lib keine Instanz von Semigroup für bereitstellt mutable.Map, aber Sie könnten sie von einer für unveränderlich ableiten:

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)

Beachten Sie jedoch, dass dies tatsächlich eine veränderbare Karte in eine unveränderliche konvertiert, dann zusammengeführt und dann wieder in die veränderliche Karte konvertiert, sodass es wahrscheinlich nicht sehr effizient ist.

1 jwvh Dec 01 2020 at 14:29

Das könnte es tun.

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
      }
    }

Ich habe es nicht mit mutableMaps getestet, aber es sollte mehr oder weniger gleich funktionieren.