0

I've built an abstraction such as this:

class Thing(val messages: Map[String, Seq[String]]) { 
  def and(that: Thing): Thing = {
    new Thing(this.messages ++ that.messages)
  }
}

this's map of String -> Seq[String] needs to be merged with that's map.

This is the best way I can think of to do this:

def and(that: Thing): Thing = {
  val keys = this.messages.keys ++ that.messages.keys
  val tuples = keys map {
    case key: String =>
      val theseMessages = this.messages.getOrElse(key, Seq[String]())
      val thoseMessages = that.messages.getOrElse(key, Seq[String]())
      (key, theseMessages ++ thoseMessages)
  }
  new Thing(tuples.toMap)
}
Scoobie
  • 1,097
  • 2
  • 12
  • 22
  • Possible duplicate of [this question](http://stackoverflow.com/questions/1262741/scala-how-to-merge-a-collection-of-maps). You can use the code provided in the accepted answerwith the combinator function being `{_ ++ _}` – Ratan Sebastian Oct 21 '12 at 03:38

3 Answers3

3

Here's a version that uses groupBy:

def and(that: Thing): Thing = {
  new Thing(
    (this.messages.toIndexedSeq ++ that.messages)
      .groupBy(_._1)
      .map { case (k,vs) => k -> vs.flatMap(_._2) }
      .toMap)
}
dhg
  • 52,383
  • 8
  • 123
  • 144
1

The Scalaz library includes this function, the |+| (semigroup append) operator. Note that you will need to choose a more specific collection type than Seq, i.e. one that has a semigroup instance, so I've used List.

import scalaz._, Scalaz._

class Thing(val messages: Map[String, List[String]]) { 
  def and(that: Thing): Thing =
    new Thing(this.messages |+| that.messages)
}

You could also define a semigroup instance for Thing, and use the |+| operator to combine Things.

case class Thing(val messages: Map[String, List[String]])

implicit val ThingSemigroup = new Semigroup[Thing] {
  def append(t1: Thing, t2: => Thing) = new Thing(t1.messages |+| t2.messages)
}

Thing(Map("foo" -> List("bar"))) |+| Thing(Map("foo" -> List("baz")))
// Thing(Map(foo -> List(bar, baz)))
Ben James
  • 121,135
  • 26
  • 193
  • 155
0

A more specific, slightly modified version of the answer I linked to in the comment which might make the approach a little clearer:

def and(that: Thing): Thing = {
  new Thing(this.messages.foldLeft(that.messages) { case (out, (keyToAdd, valuesToAdd)) =>
    out ++ Map(keyToAdd -> out.get(keyToAdd).map(_ ++ valuesToAdd).getOrElse(valuesToAdd))
  })
}

Also has the advantage of not looping over one of the maps.

Ratan Sebastian
  • 1,892
  • 14
  • 23