0

In Scala, if we have a MultiMap which maps String to Set[String], for example:

val a = Map(
  "Account1" -> Set("Cars", "Trucks"),
  "Account2" -> Set("Trucks", "Boats")
)

What's an elegant way of inverting / reversing it to end up with:

Map(
  "Boats" -> Set("Account2"),
  "Cars" -> Set("Account1"),
  "Trucks" -> Set("Account1", "Account2")
)
David B
  • 455
  • 6
  • 13

1 Answers1

0

Sorry for the long liner, you can break it down, this is just a quick & dirty solution but it does the work

scala> a.toList.flatMap{ case (k,v:Set[_]) => v map (x => (x,k))}.groupBy(_._1).map{ case (k,v:List[(String,String)]) => (k,v.map(_._2).toSet)} 
res45: scala.collection.immutable.Map[String,scala.collection.immutable.Set[String]] = Map(Boats -> Set(Account2), Trucks -> Set(Account1, Account2), Cars -> Set(Account1))

or if you prefer the readable version (the same as the above just with breakdown to lines) :) :

scala> val aTuples =  a.toList.flatMap{ case (k,v:Set[_]) => v map (x => (x,k))}
aTuples: List[(String, String)] = List((Cars,Account1), (Trucks,Account1), (Trucks,Account2), (Boats,Account2))

scala> val grouped = aTuples.groupBy(_._1)
grouped: scala.collection.immutable.Map[String,List[(String, String)]] = Map(Boats -> List((Boats,Account2)), Trucks -> List((Trucks,Account1), (Trucks,Account2)), Cars -> List((Cars,Account1)))

scala> val flattenAndToSet = grouped.map{ case (k,v:List[(String,String)]) => (k,v.map(_._2).toSet)}
flattenAndToSet: scala.collection.immutable.Map[String,scala.collection.immutable.Set[String]] = Map(Boats -> Set(Account2), Trucks -> Set(Account1, Account2), Cars -> Set(Account1))
igx
  • 4,101
  • 11
  • 43
  • 88
  • 1
    Thanks. I've learned a couple of things from your answer and changed what I had (using "zip" for tuples) to get to this 3 lines. I had 5 more verbose lines before, so I appreciate your help. `val aTuples = a.toList.flatMap(t => List.fill(t._2.size)(t._1) zip t._2) val grouped = aTuples.map(_.swap).groupBy(_._1) val flattenAndToSet = grouped.map{ case (k,v:List[(String,String)]) => (k,v.map(_._2).toSet)}` I'll mark your solution as an answer if we don't have something even shorter soon. I was hoping for "dynamically mixin MultiMap trait (I can't get that to work!) then call .invert" :) – David B Nov 14 '16 at 09:58