2010-12-01 20 views
9

Tengo una estructura de datos de tres niveles (sangría y saltos de línea para facilitar la lectura):Cómo acceder y actualizar un valor en un mapa mutable del mapa de mapas

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

Acceso a los datos íntimos (puntuaciones) requiere mucha escritura:

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

¿Hay alguna manera menos detallada para modificar el valor de los puntajes?

Soy un novato Scala, por lo que todas las demás observaciones del código anterior también son bienvenidos.

Respuesta

21

Usted no tiene que utilizar "aplicar" sólo lo hacen normalmente con "()"

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

que funciona en este ejemplo, pero no va a funcionar si no ha sido ya definido m ("normal") ("casa"). – Mark

10

Usted puede escribir

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

como

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

Sin embargo, me No estoy seguro si tal estructura es una buena idea. Quizás debería considerar encapsular esta funcionalidad en una clase especializada.

3

Adición de una función de ayuda local es siempre una buena manera de reducir la duplicación de código:

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

[Editar hizo código más estricto]

+0

¡listo! (Yo sólo espero que no demasiado inteligente :) Me tomó un par de lecturas antes de comprender lo que está pasando aquí. Gran respuesta, gracias! – user272735

3

menos detallado:

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) 

Esto se aprovecha de la hecho de que tanto apply y update tienen azúcares sintácticas para ellos como se ha visto anteriormente. Todavía más corto:

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

que aprovecha el azúcar sintáctica para captadores y definidores (el captador definición necesidad existe para la definición de establecimiento al trabajo).

+0

Esta es también una respuesta muy útil - gracias! Le deseo muchos votos a favor también. Captador/definidor explicó para principiantes: http://www.dustinmartin.net/2009/10/getters-and-setters-in-scala/ – user272735