在Scala 2.8中,我有一个不可变的映射,每个键都有多个值:
Map[T,Iterable[U]]
是否有优越的代表性?其次,你将如何从
生成这样的地图Iterable[(T,U)]
?我目前正在使用:
def toGroupedMap[T,U](vals: Iterable[(T,U)]): Map[T,Iterable[U]] =
vals.groupBy(_._1).map({ case (s,it) => (s,it.map(_._2)) }).toMap
哪个有效,但感觉笨重。
编辑:我应该指定我正在使用不可变数据。是否存在与MultiMap不可变的等价物?
答案 0 :(得分:4)
如果你真的不需要不变性,那么正如其他人所说,MultiMap
是可行的方法。如果你确实需要不变性,那么你采取的方法就像其他任何事情一样简单;没有任何内置的东西(AFAIK),任何不可变的MultiMap的创建都会比你在那里的方法花费更多的工作。
表示是否优越取决于您的使用情况。您是否经常想要使用与一个键对应的所有值进行操作?您可以多次在地图中插入相同的值吗?如果两者都是,那么你的代表是正确的。
如果您希望在一个键上最多插入一次相同的值,那么您应该使用Set[U]
而不是Iterable[U]
(可以通过将.toSet
添加到{{1}来轻松完成}})。
如果你不喜欢处理集合/ iterables并且只是忍受它(即你真的只是拥有键值对而不是key-setofvalues对),你必须编写一个包装类提供单个地图界面的地图,用+, - 和迭代器做正确的事。
这是一个比我预期的更长的例子(这里格式化为剪切并粘贴到REPL中):
it.map(_._2)
我们可以看到这样可以按预期工作:
import scala.collection._
class MapSet[A,B](
val sets: Map[A,Set[B]] = Map[A,Set[B]]()
) extends Map[A,B] with MapLike[A,B,MapSet[A,B]] {
def get(key: A) = sets.getOrElse(key,Set[B]()).headOption
def iterator = new Iterator[(A,B)] {
private val seti = sets.iterator
private var thiskey:Option[A] = None
private var singles:Iterator[B] = Nil.iterator
private def readyNext {
while (seti.hasNext && !singles.hasNext) {
val kv = seti.next
thiskey = Some(kv._1)
singles = kv._2.iterator
}
}
def hasNext = {
if (singles.hasNext) true
else {
readyNext
singles.hasNext
}
}
def next = {
if (singles.hasNext) (thiskey.get , singles.next)
else {
readyNext
(thiskey.get , singles.next)
}
}
}
def +[B1 >: B](kv: (A,B1)):MapSet[A,B] = {
val value:B = kv._2.asInstanceOf[B]
new MapSet( sets + ((kv._1 , sets.getOrElse(kv._1,Set[B]()) + value)) )
}
def -(key: A):MapSet[A,B] = new MapSet( sets - key )
def -(kv: (A,B)):MapSet[A,B] = {
val got = sets.get(kv._1)
if (got.isEmpty || !got.get.contains(kv._2)) this
else new MapSet( sets + ((kv._1 , got.get - kv._2)) )
}
override def empty = new MapSet( Map[A,Set[B]]() )
}
(虽然如果你想在++之后恢复MapSet,你需要覆盖++; Map层次结构没有自己的构建器来处理这样的事情。)
答案 1 :(得分:2)
查看Map的MultiMap混合。
答案 2 :(得分:0)
您需要的是多图。这是一个创建一个然后从List [(String,Int)]添加条目的示例。我确信这是一种更漂亮的方式。
scala> val a = new collection.mutable.HashMap[String, collection.mutable.Set[Int]]() with collection.mutable.MultiMap[String, Int]
a: scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int] = Map()
scala> List(("a", 1), ("a", 2), ("b", 3)).map(e => a.addBinding(e._1, e._2))
res0: List[scala.collection.mutable.HashMap[String,scala.collection.mutable.Set[Int]] with scala.collection.mutable.MultiMap[String,Int]] = List(Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)), Map(a -> Set(1, 2), b -> Set(3)))
scala> a("a")
res2: scala.collection.mutable.Set[Int] = Set(1, 2)