簡體   English   中英

為將集合元素添加到Map [K,Set [V]]的創建狀態轉換的更好方法

[英]Better way to create state transformation for adding set elements to Map[K, Set[V]]

我有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來實現這一目標?

您可以編寫一個適配器以“展平”該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()

但是,這可能不是您想要的。

香草

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)

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM