Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Invert a Map (String -> List) in Scala

I have a Map[String, List[String]] and I want to invert it. For example, if I have something like

  "1" -> List("a","b","c")
  "2" -> List("a","j","k")
  "3" -> List("a","c")

The result should be

  "a" -> List("1","2","3")
  "b" -> List("1")
  "c" -> List("1","3")
  "j" -> List("2")
  "k" -> List("2")

I've tried this:

  m.map(_.swap)

But it returns a Map[List[String], String]:

  List("a","b","c") -> "1"
  List("a","j","k") -> "2" 
  List("a","c") -> "3"
like image 764
Kuranes Avatar asked Nov 15 '17 20:11

Kuranes


People also ask

How do I convert a list to map in Scala?

To convert a list into a map in Scala, we use the toMap method. We must remember that a map contains a pair of values, i.e., key-value pair, whereas a list contains only single values. So we have two ways to do so: Using the zipWithIndex method to add indices as the keys to the list.

Is map mutable in Scala?

Maps are classified into two types: mutable and immutable. By default Scala uses immutable Map. In order to use mutable Map, we must import scala.

What is Scala collection mutable map?

There are two kinds of Maps, the immutable and the mutable. The difference between mutable and immutable objects is that when an object is immutable, the object itself can't be changed. By default, Scala uses the immutable Map. If you want to use the mutable Map, you'll have to import scala.


2 Answers

Map inversion is a little more complicated.

val m = Map("1" -> List("a","b","c")
           ,"2" -> List("a","j","k")
           ,"3" -> List("a","c"))

m flatten {case(k, vs) => vs.map((_, k))} groupBy (_._1) mapValues {_.map(_._2)}
//res0: Map[String,Iterable[String]] = Map(j -> List(2), a -> List(1, 2, 3), b -> List(1), c -> List(1, 3), k -> List(2))

Flatten the Map into a collection of tuples. groupBy will create a new Map with the old values as the new keys. Then un-tuple the values by removing the key (previously value) elements.

like image 152
jwvh Avatar answered Oct 11 '22 12:10

jwvh


An alternative that does not rely on strange implicit arguments of flatten, as requested by yishaiz:

val m = Map(
  "1" -> List("a","b","c"),
  "2" -> List("a","j","k"),
  "3" -> List("a","c"),
)

val res = (for ((digit, chars) <- m.toList; c <- chars) yield (c, digit))
  .groupBy(_._1)          // group by characters
  .mapValues(_.unzip._2)  // drop redundant digits from lists

res foreach println

gives:

(j,List(2))
(a,List(1, 2, 3))
(b,List(1))
(c,List(1, 3))
(k,List(2))
like image 45
Andrey Tyukin Avatar answered Oct 11 '22 11:10

Andrey Tyukin