mutable.Map deep merge

Dec 01 2020

Apakah ada cara ringkas untuk menggabungkan dua peta yang bisa berubah secara mendalam di 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 = ???

Contoh:

SAYA.

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

AKU AKU AKU.

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

Yaitu jika ada kunci yang sama disajikan di kedua peta maka nilai yang sesuai dengan kunci ( List[Int]) digabungkan.

Apakah ada cara untuk mengimplementasikannya secara ringkas dengan menghindari banyak pemeriksaan apakah kunci tertentu ada atau tidak di peta lain? Menggunakan libs FP seperti scalaz atau kucing juga tidak masalah.

Jawaban

3 KrzysztofAtłasik Dec 01 2020 at 15:40

Saya menambahkan jawaban lain menggunakan kucing.

Yang Anda gambarkan sebenarnya adalah perilaku kucing . Jadi Anda bisa menggunakan |+|operator gabung ( ) untuk menggabungkan peta secara mendalam:

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

Masalahnya adalah kucing lib tidak menyediakan instance Semigroup untuk mutable.Map, tetapi Anda dapat memperolehnya dari salah satu untuk tidak dapat diubah:

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)

Namun perlu diingat, ini sebenarnya mengubah peta yang dapat berubah menjadi tidak dapat diubah kemudian melakukan penggabungan dan kemudian mengubahnya kembali ke peta yang dapat berubah, jadi itu mungkin tidak terlalu efisien.

1 jwvh Dec 01 2020 at 14:29

Ini mungkin berhasil.

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

Saya belum mengujinya dengan mutableMaps tetapi seharusnya berfungsi kurang lebih sama.