How to access and update a value in a mutable map of map of maps
Solution 1
You don't have to use "apply" just do it normally with "()"
m("normal")("home")("scores") = 1
Solution 2
You can write
m("normal").apply("home").apply("scores")
as
m("normal")("home")("scores")
However I'm not sure if such a structure is a good idea. Maybe you should consider encapsulating this functionality in a specialized class.
Solution 3
Adding a local helper function is always good way to reduce code duplication:
class MapExamplesSO {
def test {
import scala.collection.mutable.Map
// The m map is essentially an accumulator
var m = Map("normal" ->
Map("home" -> Map("wins" -> 0, "scores" -> 0),
"away" -> Map("wins" -> 0, "scores" -> 0)))
//Local Helper returns (Old, New)
def updateScore(k1 : String,k2 : String,k3 : String)
(f : Int => Int) : (Int, Int) = {
val old = m(k1)(k2)(k3)
m(k1)(k2)(k3) = f(old)
(old, m(k1)(k2)(k3))
}
assert(m("normal")(home")("scores") === 0)
assert(updateScore("normal","home","scores")(_+1)._2 === 1)
assert(updateScore("normal","home","scores")(_+2)._2 === 3)
}
}
[Edit made code tighter]
Solution 4
Less verbose:
assert(m("normal")("home")("scores") === 0)
val s1 = m("normal")("home")("scores") + 1
m("normal")("home")("scores") = s1
assert(m("normal")("home")("scores") === 1)
val s2 = m("normal")("home")("scores") + 2
m("normal")("home")("scores") = s2
assert(m("normal")("home")("scores") === 3)
This takes advantage of the fact that both apply
and update
have syntactic sugars for them as seen above. Shorter still:
// On REPL, put both these definitions inside an object instead
// of entering them on different lines
def scores = m("normal")("home")("scores")
def scores_=(n: Int) = m("normal")("home")("scores") = n
assert(scores === 0)
val s1 = scores + 1
scores = s1
assert(scores === 1)
val s2 = scores + 2
scores = s2
// Just so you see these updates are being made to the map:
assert(m("normal")("home")("scores") === 3)
Which takes advantage of the syntactic sugar for getters and setters (the getter definition must exist for the setter definition to work).
user272735
Updated on June 04, 2022Comments
-
user272735 almost 2 years
I've a three-level data structure (indentation and line breaks for readability):
scala> import scala.collection.mutable.Map import scala.collection.mutable.Map scala> val m = Map("normal" -> Map("home" -> Map("wins" -> 0, "scores" -> 0), "away" -> Map("wins" -> 0, "scores" -> 0))) m: scala.collection.mutable.Map[java.lang.String, scala.collection.mutable.Map[java.lang.String, scala.collection.mutable.Map[java.lang.String,Int]]] = Map((normal,Map(away -> Map(wins -> 0, scores -> 0), home -> Map(wins -> 0, scores -> 0))))
Accessing the innermost data (scores) requires a lot of typing:
import org.scalatest.{Assertions, FunSuite} class MapExamplesSO extends FunSuite with Assertions { test("Update values in a mutable map of map of maps") { import scala.collection.mutable.Map // The m map is essentially an accumulator val m = Map("normal" -> Map("home" -> Map("wins" -> 0, "scores" -> 0), "away" -> Map("wins" -> 0, "scores" -> 0) ) ) // // Is there a less verbose way to increment the scores ? // assert(m("normal").apply("home").apply("scores") === 0) val s1 = m("normal").apply("home").apply("scores") + 1 m("normal").apply("home").update("scores", s1) assert(m("normal").apply("home").apply("scores") === 1) val s2 = m("normal").apply("home").apply("scores") + 2 m("normal").apply("home").update("scores", s2) assert(m("normal").apply("home").apply("scores") === 3) } }
Is there a less verbose way to modify the value of scores ?
I'm a Scala newbie, so all other observations of the code above are also welcome.
-
user272735 over 13 yearsClever ! (I just hope not too clever:) Took me a few readings before I realised what is going on here. Great answer, thanks !
-
user272735 over 13 yearsThis is also a very helpful answer - thanks ! I wish many upvotes to you too. Getter/setter explained for beginners: dustinmartin.net/2009/10/getters-and-setters-in-scala
-
Mark over 7 yearsThat works in this example, but won't work if m("normal")("home") hasn't already been defined.