[英]Better way to create state transformation for adding set elements to Map[K, Set[V]]
I have Map[K, Set[V]]
and I am using Scalaz Lenses and State to add elements to it. 我有
Map[K, Set[V]]
,正在使用Scalaz Lenses和State向其中添加元素。
So far, I see myself doing this repeatedly: 到目前为止,我看到自己反复这样做:
myMapLens.member(key) %= {
case Some(vals) => Some(vals + newValue)
case None => Some(Set(newValue))
}
Is there a better way to do this using Scalaz ? 有没有更好的方法使用Scalaz做到这一点? Casting my value set into
Some(...)
every time seems wasteful. 每次将我的值转换为
Some(...)
似乎都是浪费。
Specifically, is there a way to compose Scalaz MapLens and SetLens to achieve this ? 具体来说,有没有一种方法可以组成Scalaz MapLens和SetLens来实现这一目标?
You can write an adapter to "flatten" the Option
: 您可以编写一个适配器以“展平”该
Option
:
import scalaz._, Scalaz._
def noneZero[A: Monoid]: Lens[Option[A], A] = Lens.lensg(_ => Some(_), _.orZero)
This is a little more generic than you need, but has the same behavior for your use case: 这比您需要的通用一些,但是对于您的用例具有相同的行为:
val myMapLens = Lens.lensId[Map[String, Set[Int]]]
val myLens = myMapLens.member("foo").andThen(noneZero).contains(1)
You could of course use any of the other methods on SetLens
— contains
just makes for a nice demonstration: 您当然可以在
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())
The following is arguably a slightly more principled way to write the adapter: 可以说,以下是编写适配器的原则性更强的方法:
def noneZero[A: Monoid: Equal]: Lens[Option[A], A] = Lens.lensg(
_ => a => a.ifEmpty[Option[A]](none)(some(a)),
_.orZero
)
This behaves the same except that unsetting the last value in a set removes it from the map: 除了取消设置集合中的最后一个值将其从地图中删除外 ,这的行为相同。
scala> myLens.set(Map("foo" -> Set(1)), false)
res5: Map[String,Set[Int]] = Map()
This may not be what you want, though. 但是,这可能不是您想要的。
Vanilla 香草
myMap + (key -> myMap.get(key).fold(Set(newValue))(_ + newValue))
seems easier. 似乎更容易。
So does writing an extension method, and there it's worth a little extra work to avoid needless reconstruction of anything: 编写扩展方法也是如此,为避免不必要的重构,值得做一些额外的工作:
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))
}
}
Now you can merrily myMap setAdd (key, newValue)
. 现在,您可以愉快地
myMap setAdd (key, newValue)
。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.