【问题标题】:Many-value map in ScalaScala 中的多值映射
【发布时间】:2011-01-12 17:11:59
【问题描述】:

在 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 等价的不可变对象?

【问题讨论】:

  • 好吧,我想不出任何改进你所拥有的东西的方法。

标签: scala scala-2.8 multimap


【解决方案1】:

查看 Map 的 MultiMap mix-in。

【讨论】:

  • 我避开了 MultiMap,因为它是可变的并且似乎没有不可变的对应物。
【解决方案2】:

您需要的是多张地图。这是一个创建一个然后从 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)

【讨论】:

    【解决方案3】:

    如果您真的不需要不变性,那么正如其他人所说,MultiMap 是要走的路。如果您确实需要不变性,那么您所采用的方法与其他任何方法一样简单;没有内置任何东西(AFAIK),任何不可变 MultiMap 的创建都将比您现有的方法花费更多的工作。

    表示是否优越取决于您的使用情况。你是不是经常想做一键对应所有值的事情?您可以在地图中多次插入相同的值吗?如果两者都是,那么您的代表是正确的。

    如果您希望在一个键上最多插入一次相同的值,那么您应该使用Set[U] 而不是Iterable[U](可以通过将.toSet 添加到it.map(_._2) 轻松完成)。

    如果你不喜欢处理集合/可迭代对象并且只是忍受它(即你真的宁愿只拥有键值对而不是键值对),你必须编写一个包装类显示单个地图界面并使用 +、- 和迭代器执行正确操作的地图。

    这是一个比我预期的要长一点的例子(这里的格式是为了剪切和粘贴到 REPL 中):

    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]]() )
    }
    

    我们可以看到这可以像这样工作:

    scala> new MapSet() ++ List(1->"Hi",2->"there",1->"Hello",3->"Bye")
    res0: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 3 -> Bye)
    
    scala> res0 + (2->"ya")
    res1: scala.collection.Map[Int,java.lang.String] = Map(1 -> Hi, 1 -> Hello, 2 -> there, 2 -> ya, 3 -> Bye)
    
    scala> res1 - 1
    res2: scala.collection.Map[Int,java.lang.String] = Map(2 -> there, 2 -> ya, 3 -> Bye)
    

    (但如果您想在 ++ 之后恢复 MapSet,则需要覆盖 ++;地图层次结构没有自己的构建器来处理此类事情)。

    【讨论】:

      猜你喜欢
      • 2013-02-02
      • 2021-06-18
      • 1970-01-01
      • 2010-11-12
      • 1970-01-01
      • 2012-02-26
      • 2011-03-26
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多