Scala:对元组列表进行分组

5
我是一名有用的助手,可以为您进行文本翻译。以下是您需要翻译的内容:

我需要以某种独特的方式对元组列表进行分组。

例如,如果我有

val l = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))

然后我应该按照第二个值对列表进行分组,并将其映射到第一个值的集合。因此,结果应该是:
Map(2 -> Set(1,4), 3 -> Set(2,10))

到目前为止,我想到了这个。
l groupBy { p => p._2 } mapValues { v => (v map { vv => vv._1 }).toSet } 

这样做是可行的,但我认为应该有更高效的方法...

1
我认为你的解决方案很好。 - serejja
1个回答

3

这类似于这个问题。基本上,就像@serejja所说的那样,你的方法是正确的,也是最简洁的方法。你可以使用collection.breakOut作为构建器工厂参数传递给最后一个map,从而节省获取Set类型所需的额外迭代:

l.groupBy(_._2).mapValues(_.map(_._1)(collection.breakOut): Set[Int])

除非你真的需要提高性能,否则不应该超越这个限制。


否则,一个通用的toMultiMap函数可能是这样的,它允许你控制值集合的类型:

import collection.generic.CanBuildFrom
import collection.mutable

def toMultiMap[A, K, V, Values](xs: TraversableOnce[A])
    (key: A => K)(value: A => V)
    (implicit cbfv: CanBuildFrom[Nothing, V, Values]): Map[K, Values] = {
  val b = mutable.Map.empty[K, mutable.Builder[V, Values]]
  xs.foreach { elem =>
    b.getOrElseUpdate(key(elem), cbfv()) += value(elem)
  }
  b.map { case (k, vb) => (k, vb.result()) } (collection.breakOut)
}

它的作用是,在构建阶段使用可变的 Map,并首先在可变的 Builder 中收集值(该 Builder 是由 CanBuildFrom 实例提供的)。在迭代所有输入元素完成后,将该可变的 builder 值映射转换为值集合类型的不可变映射(再次使用 collection.breakOut 技巧立即获取所需的输出集合)。
例如:
val l                    = List((1,2,3),(4,2,5),(2,3,3),(10,3,2))
val v                    = toMultiMap(l)(_._2)(_._1)  // uses Vector for values
val s: Map[Int, Set[Int] = toMultiMap(l)(_._2)(_._1)  // uses Set for values

因此,您的注释结果类型将指导值类型的类型推断。如果您没有注释结果,则Scala将选择Vector作为默认集合类型。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接