Search code examples
scaladictionarygroup-bysetmutable

Scala: Why the mutable value inside Map cannot be changed if the Map is created from GroupBy


I want to create a Map object with the key as an integer and the value as a mutable Set. However, when I create my Map object from GroupBy function, the value in my mutable Set can not be changed anymore. Can anyone tell my why this happen?

import scala.collection.mutable

val groupedMap: Map[Int, mutable.Set[Int]] = 
    List((1,1),(1,2),(2,3))
        .groupBy(_._1)
        .mapValues(_.map(_._2).to[mutable.Set])

val originalMap: Map[Int, mutable.Set[Int]] =
    Map(1 -> mutable.Set(1, 2), 2 -> mutable.Set(3))

println(groupedMap) // Map(1 -> Set(1, 2), 2 -> Set(3))
println(originalMap) // Map(1 -> Set(1, 2), 2 -> Set(3))

groupedMap(1) += 99
originalMap(1) += 99

println(groupedMap) // Map(1 -> Set(1, 2), 2 -> Set(3))  <- HERE IS THE PROBLEM, THE VALUE 99 CAN NOT BE ADDED TO MY MUTABLE SET!
println(originalMap) // Map(1 -> Set(99, 1, 2), 2 -> Set(3))

Solution

  • .mapValues is lazy, meaning the function you give it is executed every time you access the value, so, when you do groupedMap(1) += 99, it runs your conversion, returns a Set to which you add 99, and discards it.

    Then, when you print it, it runs the conversion again ... and prints the original contents.

    If the above does not seem clear, try running this snippet as an illustration:

     val foo = Map("foo" -> "bar")
       .mapValues { _ => println("mapValues"); "baz" }
    
     println(foo("foo") + foo("foo"))
    

    This is one of many problems you run into when using mutable data. Don't do it. In 99% of use cases in scala it is not needed. So, it is better to just pretend it does not exist at all, until you get enough grasp of the language to be able to determine definitively the remaining 1%.