我有Map[K, Set[V]]
我正在使用Scalaz Lenses和State为其添加元素。
到目前为止,我看到自己反复这样做了:
myMapLens.member(key) %= {
case Some(vals) => Some(vals + newValue)
case None => Some(Set(newValue))
}
使用Scalaz有更好的方法吗?每次将我的值设置为Some(...)
似乎都很浪费。
具体来说,有没有办法组合Scalaz MapLens和SetLens来实现这个目标?
答案 0 :(得分:3)
您可以编写一个适配器来#34;展平" Option
:
import scalaz._, Scalaz._
def noneZero[A: Monoid]: Lens[Option[A], A] = Lens.lensg(_ => Some(_), _.orZero)
这比您需要的更通用,但对您的用例具有相同的行为:
val myMapLens = Lens.lensId[Map[String, Set[Int]]]
val myLens = myMapLens.member("foo").andThen(noneZero).contains(1)
你当然可以使用SetLens
上的任何其他方法 - contains
只是为了一个很好的演示:
scala> myLens.get(Map("foo" -> Set(1)))
res0: Boolean = true
scala> myLens.get(Map("bar" -> Set(1)))
res1: Boolean = false
scala> myLens.set(Map("foo" -> Set(2)), true)
res2: Map[String,Set[Int]] = Map(foo -> Set(2, 1))
scala> myLens.set(Map("bar" -> Set(2)), true)
res3: Map[String,Set[Int]] = Map(bar -> Set(2), foo -> Set(1))
scala> myLens.set(Map("foo" -> Set(1)), false)
res4: Map[String,Set[Int]] = Map(foo -> Set())
以下可以说是一种稍微更原则的编写适配器的方法:
def noneZero[A: Monoid: Equal]: Lens[Option[A], A] = Lens.lensg(
_ => a => a.ifEmpty[Option[A]](none)(some(a)),
_.orZero
)
除外,其行为与相同,取消设置集合中的最后一个值会将其从地图中删除:
scala> myLens.set(Map("foo" -> Set(1)), false)
res5: Map[String,Set[Int]] = Map()
但这可能不是你想要的。
答案 1 :(得分:1)
香草
myMap + (key -> myMap.get(key).fold(Set(newValue))(_ + newValue))
似乎更容易。
编写扩展方法也是如此,为了避免不必要的重建,它值得做一些额外的工作:
implicit class MapsToSetsCanAdd[K,V](map: Map[K, Set[V]]) {
def setAdd(key: K, value: V) = map.get(key) match {
case Some(set) => if (set contains value) map else map + (key -> (set + value))
case None => map + (key -> Set(value))
}
}
现在你可以愉快myMap setAdd (key, newValue)
。