2010-12-01 3 views
9

J'ai une structure de données à trois niveaux (casse indentation et ligne pour une meilleure lisibilité):Comment accéder et mettre à jour une valeur dans une carte mutable de carte de cartes

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)))) 

Accès aux données les plus intimes (scores) nécessite beaucoup de dactylographie:

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) 
    } 
} 

Existe-t-il une manière moins verbeuse de modifier la valeur des scores? Je suis un débutant Scala, donc toutes les autres observations du code ci-dessus sont également les bienvenues.

Répondre

21

Vous ne devez pas utiliser "appliquer" juste le faire normalement "()"

m("normal")("home")("scores") = 1 
+1

Cela fonctionne dans cet exemple, mais ne fonctionnera pas si m ("normal") ("home") n'a pas déjà été défini. – Mark

10

Vous pouvez écrire

m("normal").apply("home").apply("scores") 

comme

m("normal")("home")("scores") 

Cependant, je Je ne suis pas sûr si une telle structure est une bonne idée. Peut-être que vous devriez envisager d'encapsuler cette fonctionnalité dans une classe spécialisée.

3

Ajout d'une fonction d'assistance locale est toujours une bonne façon de réduire la duplication de code:

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) 
    } 
} 

[Modifier fait le code plus serré]

+0

Clever! (J'espère juste pas trop intelligent :) Il m'a fallu quelques lectures avant de réaliser ce qui se passait ici. Bonne réponse, merci! – user272735

3

Moins bavard:

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) 

Cela profite du fait que les deux apply et update ont des sucres syntaxiques pour eux comme vu ci-dessus. Plus court encore:

// 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) 

qui profite du sucre syntaxique pour accesseurs (getter définition doit existe pour la définition setter au travail).

+0

Ceci est également une réponse très utile - merci! Je te souhaite aussi beaucoup de votes positifs. Getter/setter expliqué pour les débutants: http://www.dustinmartin.net/2009/10/getters-and-setters-in-scala/ – user272735

Questions connexes