给定一个Map[Int, Set[Int]]
我怎么能修改地图的单个值,产生一个新的过程中,例如:
val x = Map(1 -> Set(1,2,3))
x(1) + 5 // This creates a new Set, but not a new Map
val y = x(1) change { x => x + 5 }
// The previous functionality is what I'm looking for
// z: Set[Int]] = List(Set(1, 2, 3, 5))
Answer 1:
缩放2.10:
implicit class ChangeableMap[K,V]( val m: Map[K,V] ) extends AnyVal {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
一些测试:
scala>val x = Map(1 -> Set(1,2,3), 2 -> Set(4,5))
x: scala.collection.immutable.Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3), 2 -> Set(4, 5))
scala> x.change(1) { x => x + 5 }
res1: Map[Int,scala.collection.immutable.Set[Int]] = Map(1 -> Set(1, 2, 3, 5), 2 -> Set(4, 5))
如果您在斯卡拉2.9的时候,这会做:
class ChangeableMap[K,V]( m: Map[K,V] ) {
def change( k: K )( transform: V => V ): Map[K,V] = {
m.get( k ).map{ v => m + (k-> transform(v)) }.getOrElse( m )
}
}
implicit def toChangeableMap[K,V]( m: Map[K,V] ) = new ChangeableMap[K,V]( m )
Answer 2:
由于罗宾·格林指出,镜片这份工作做。 事实上,你想有一个部分镜头,因为地图是关键的部分功能 - >值。
Scalaz 7包括mapVPLens
起作用以使一个部分透镜( PLens
)的值在所选择的键:
import scalaz.PLens._
val x = Map(1 -> Set(1,2,3))
mapVPLens(1) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1, 2, 3, 5))
修改在一个不存在的键的值将没有任何效果:
mapVPLens(9) mod ((_: Set[Int]) + 5, x) // Map(1 -> Set(1,2,3))
Answer 3:
使用的镜头!
然而,Scalaz 6,它定义了镜头,没有您的情况,这意味着你稍微工作特定的预先制作的镜头-但如果你的地图是包含在另一个对象反过来,它确实有(以及隐藏)对这种情况的支持 。 而Scalaz 7将有独立的地图中的镜头。
此外,镜头只是对功能,不需要语言的支持,所以你可以只滚你自己。
Answer 4:
这里有一个从我们的代码库。
/**
* Alters a value in a map.
*
* modifyMap :: Map k v -> k -> (Maybe v -> Maybe v) -> Map k v
* See Haskell's Data.Map.alter
*
* @param m the map to modify
* @param key the key to modify the value of
* @param mod a function that takes the existing value (if any) and returns an optional new value
*
* @return the modified map
*/
def modifyMap[K,V](m: Map[K,V], key: K)
(mod: (Option[V] ⇒ Option[V])): Map[K,V] = {
mod(m.get(key)) match {
case Some(newVal) ⇒ m + (key → newVal)
case None ⇒ m - key
}
}
这里是你如何使用它:
modifyMap(myMap, "someKey") {
case Some(someVal) =>
// present
if (condition)
Some(valueDerivedFrom(someVal)) // provide a new mapping for someKey
else
None // someKey will now be unset
case None =>
// wasn't present
if (condition)
Some(newValue) // provide a new value for someKey
else
None // leave someKey unset
}
Answer 5:
解决这一问题将是以下(感谢一个非常地道的方式尤巴生 ):
val x = Map(1 -> Set(1,2,3), 2 -> Set(1), 3 -> Set(5))
x.map { case (1, v) => (1, v + 5); case x => x }
// res0: Map(1 -> Set(1, 2, 3, 5))
或很好地包装成一个类以及隐式:
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.map {
case (`index`, v:V) => (index, f(v))
case x => x
}
}
object ChangeableMap {
implicit def fromMap[K,V](map:Map[K,V]) = new ChangeableMap(map)
}
与先前的声明,下面的工作:
x.change(1) { x => x + 5 }
x.change(1) { _ + 5 }
// res1: Map(1 -> Set(1, 2, 3, 5))
请注意,这可能不是最快的解决方案,因为斯卡拉将(可能,还没有证实)遍历整个地图!
一个可能更快地实现将是以下(不过,如果它实际上更快我没有验证):
class ChangeableMap[K,V](map:Map[K,V]) {
def change(index:K)(f:V => V) = map.get(index) match {
case Some(x) => map + ((index, f(x)))
case None => map
}
}
Answer 6:
我认为,最简单的方法将使用scala.collection.mutable.Map
。
import scala.collection.mutable.Map
val m = Map(1 -> Set(1,2,3))
m.update(1, m(1) + 5)
// now the Map looks like this: Map(1 -> Set(1,2,3,5))
如果你得到一个不可改变的地图,你可以简单地将其转换为使用可变之一:
val n: collection.mutale.Map(m.toSeq: _*)
这也适用周围的其他方法,如果你需要返回一个不可变的地图。
Answer 7:
如前面所提到,您可以使用部分镜头为这类问题的scalaz和单片眼镜实现它。 这里是你如何与单片眼镜做到这一点:
import monocle.syntax.taversal._ // to use |->>
import monocle.syntax.at._ // to use at
val x = Map(1 -> Set(1,2,3))
x |->> at(1) modify(_ + 5) == Map(1 -> Set(1,2,3,5))
文章来源: How to modify a value of a Map which contains Sets, returning a new Map?