Merge maps by key

前端 未结 8 2101
不思量自难忘°
不思量自难忘° 2020-12-01 05:14

Say I have two maps:

val a = Map(1 -> \"one\", 2 -> \"two\", 3 -> \"three\")
val b = Map(1 -> \"un\", 2 -> \"deux\", 3 -> \"trois\")


        
相关标签:
8条回答
  • 2020-12-01 05:54

    scala.collection.immutable.IntMap has an intersectionWith method that does precisely what you want (I believe):

    import scala.collection.immutable.IntMap
    
    val a = IntMap(1 -> "one", 2 -> "two", 3 -> "three", 4 -> "four")
    val b = IntMap(1 -> "un", 2 -> "deux", 3 -> "trois")
    
    val merged = a.intersectionWith(b, (_, av, bv: String) => Seq(av, bv))
    

    This gives you IntMap(1 -> List(one, un), 2 -> List(two, deux), 3 -> List(three, trois)). Note that it correctly ignores the key that only occurs in a.

    As a side note: I've often found myself wanting the unionWith, intersectionWith, etc. functions from Haskell's Data.Map in Scala. I don't think there's any principled reason that they should only be available on IntMap, instead of in the base collection.Map trait.

    0 讨论(0)
  • 2020-12-01 05:54

    Starting Scala 2.13, you can use groupMap which (as its name suggests) is an equivalent of a groupBy followed by map on values:

    // val map1 = Map(1 -> "one", 2 -> "two",  3 -> "three")
    // val map2 = Map(1 -> "un",  2 -> "deux", 3 -> "trois")
    (map1.toSeq ++ map2).groupMap(_._1)(_._2)
    // Map(1 -> List("one", "un"), 2 -> List("two", "deux"), 3 -> List("three", "trois"))
    

    This:

    • Concatenates the two maps as a sequence of tuples (List((1, "one"), (2, "two"), (3, "three"))). For conciseness, map2 is implicitly converted to Seq to align with map1.toSeq's type - but you could choose to make it explicit by using map2.toSeq.

    • groups elements based on their first tuple part (_._1) (group part of groupMap)

    • maps grouped values to their second tuple part (_._2) (map part of groupMap)

    0 讨论(0)
  • 2020-12-01 06:06
    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)
    
    
    0 讨论(0)
  • 2020-12-01 06:09
    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)))
    
    0 讨论(0)
  • 2020-12-01 06:09

    Here is my first approach before looking for the other solutions:

    for (x <- a) yield 
      x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten
    

    To avoid elements which happen to exist only in a or b, a filter is handy:

    (for (x <- a) yield 
      x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten).filter (_._2.size == 2)
    

    Flatten is needed, because b.get (x._1) returns an Option. To make flatten work, the first element has to be an option too, so we can't just use x._2 here.

    For sequences, it works too:

    scala> val b = Map (1 -> Seq(1, 11, 111), 2 -> Seq(2, 22), 3 -> Seq(33, 333), 5 -> Seq(55, 5, 5555))
    b: scala.collection.immutable.Map[Int,Seq[Int]] = Map(1 -> List(1, 11, 111), 2 -> List(2, 22), 3 -> List(33, 333), 5 -> List(55, 5, 5555))
    
    scala> val a = Map (1 -> Seq(1, 101), 2 -> Seq(2, 212, 222), 3 -> Seq (3, 3443), 4 -> (44, 4, 41214))
    a: scala.collection.immutable.Map[Int,ScalaObject with Equals] = Map(1 -> List(1, 101), 2 -> List(2, 212, 222), 3 -> List(3, 3443), 4 -> (44,4,41214))
    
    scala> (for (x <- a) yield x._1 -> Seq (a.get (x._1), b.get (x._1)).flatten).filter (_._2.size == 2) 
    res85: scala.collection.immutable.Map[Int,Seq[ScalaObject with Equals]] = Map(1 -> List(List(1, 101), List(1, 11, 111)), 2 -> List(List(2, 212, 222), List(2, 22)), 3 -> List(List(3, 3443), List(33, 333)))
    
    0 讨论(0)
  • 2020-12-01 06:12

    Scalaz adds a method |+| for any type A for which a Semigroup[A] is available.

    If you mapped your Maps so that each value was a single-element sequence, then you could use this quite simply:

    scala> a.mapValues(Seq(_)) |+| b.mapValues(Seq(_))
    res3: scala.collection.immutable.Map[Int,Seq[java.lang.String]] = Map(1 -> List(one, un), 2 -> List(two, deux), 3 -> List(three, trois))
    
    0 讨论(0)
提交回复
热议问题