2012-01-11 19 views
5

que quiero lograr algo como lo siguiente:asignar una sola entrada de un mapa

(_ : Map[K,Int]).mapKey(k, _ + 1) 

Y la función mapKey aplica su segundo argumento (Int => Int) sólo al valor almacenado en k. ¿Hay algo dentro de la lib estándar? Si no, apuesto a que hay algo en Scalaz.

Por supuesto que puedo escribir esta función yo mismo (m.updated(k,f(m(k))) y es muy sencillo hacerlo. Pero he resuelto este problema varias veces, ¿entonces quizás ya está hecho?

Para Scalaz me imagino algo como el siguiente código:

(m: Map[A,B]).project(k: A).map(f: B => B): Map[A,B] 
+0

¿Qué desea que suceda si el 'a' llave no está en el mapa? Nada bien? Lo estoy preguntando porque 'updated' insertará' (a -> f (m (a)) '... – huynhjl

+0

@huynhjl Para mi caso de uso, tengo un mapa con un valor predeterminado. Creo que parece más natural no ocurre nada. Tenga en cuenta que mis soluciones simples fallarán con una excepción. – ziggystar

Respuesta

3

Se podría, por supuesto, añadir

def changeForKey[A,B](a: A, fun: B => B): Tuple2[A, B] => Tuple2[A, B] = { kv => 
    kv match { 
    case (`a`, b) => (a, fun(b)) 
    case x => x 
    } 
} 

val theMap = Map('a -> 1, 'b -> 2) 
theMap map changeForKey('a, (_: Int) + 1) 
res0: scala.collection.immutable.Map[Symbol,Int] = Map('a -> 2, 'b -> 2) 

Pero esta eludir ninguna optimización de la memoria en relación con la reutilización y el acceso.

También se le ocurrió una solución scalaz en lugar prolijo e ineficiente uso de una cremallera para su project método propuesto:

theMap.toStream.toZipper.flatMap(_.findZ(_._1 == 'a).flatMap(elem => elem.delete.map(_.insert((elem.focus._1, fun(elem.focus._2)))))).map(_.toStream.toMap) 

o

(for { 
    z <- theMap.toStream.toZipper 
    elem <- z.findZ(_._1 == 'a) 
    z2 <- elem.delete 
} yield z2.insert((elem.focus._1, fun(elem.focus._2)))).map(_.toStream.toMap) 

Probablemente de poca utilidad. Solo estoy publicando para referencia.

1

Usted puede proxeneta con esto de modo que se crea un nuevo mapa basado en el anterior:

class MapUtils[A, B](map: Map[A, B]) { 
    def mapValueAt(a: A)(f: (B) => B) = map.get(a) match { 
    case Some(b) => map + (a -> f(b)) 
    case None => map 
    } 
} 

implicit def toMapUtils[A, B](map: Map[A, B]) = new MapUtils(map) 

val m = Map(1 -> 1) 
m.mapValueAt(1)(_ + 1) 
// Map(1 -> 2) 
m.mapValueAt(2)(_ + 1) 
// Map(1 -> 1) 
3

Aquí es una manera:

scala> val m = Map(2 -> 3, 5 -> 11) 
m: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11) 

scala> m ++ (2, m.get(2).map(1 +)).sequence 
res53: scala.collection.immutable.Map[Int,Int] = Map(2 -> 4, 5 -> 11) 

scala> m ++ (9, m.get(9).map(1 +)).sequence 
res54: scala.collection.immutable.Map[Int,Int] = Map(2 -> 3, 5 -> 11) 

Esto funciona porque da (A, Option[B]).sequenceOption[(A, B)]. (sequence en general resulta tipos de adentro hacia afuera. F[G[A]] => [G[F[A]] es decir, teniendo en cuenta F : Traverse y G : Applicative.)

Cuestiones relacionadas