scala 清洁元组 groupBy
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/10783772/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Cleaner tuple groupBy
提问by Tomer Gabel
I have a sequence of key-value pairs (String, Int), and I want to group them by key into a sequence of values (i.e. Seq[(String, Int)]) => Map[String, Iterable[Int]])).
我有一系列键值对(String,Int),我想按键将它们分组为一系列值(即Seq[(String, Int)]) => Map[String, Iterable[Int]]))。
Obviously, toMapisn't useful here, and groupBymaintains the values as tuples. The best I managed to come up with is:
显然,toMap在这里没有用,并将groupBy值保留为元组。我设法想出的最好的是:
val seq: Seq[( String, Int )]
// ...
seq.groupBy( _._1 ).mapValues( _.map( _._2 ) )
Is there a cleaner way of doing this?
有没有更干净的方法来做到这一点?
采纳答案by Jean-Philippe Pellet
Here's a pimp that adds a toMultiMapmethod to traversables. Would it solve your problem?
这是一个toMultiMap向可遍历对象添加方法的皮条客。它会解决你的问题吗?
import collection._
import mutable.Builder
import generic.CanBuildFrom
class TraversableOnceExt[CC, A](coll: CC, asTraversable: CC => TraversableOnce[A]) {
def toMultiMap[T, U, That](implicit ev: A <:< (T, U), cbf: CanBuildFrom[CC, U, That]): immutable.Map[T, That] =
toMultiMapBy(ev)
def toMultiMapBy[T, U, That](f: A => (T, U))(implicit cbf: CanBuildFrom[CC, U, That]): immutable.Map[T, That] = {
val mutMap = mutable.Map.empty[T, mutable.Builder[U, That]]
for (x <- asTraversable(coll)) {
val (key, value) = f(x)
val builder = mutMap.getOrElseUpdate(key, cbf(coll))
builder += value
}
val mapBuilder = immutable.Map.newBuilder[T, That]
for ((k, v) <- mutMap)
mapBuilder += ((k, v.result))
mapBuilder.result
}
}
implicit def commomExtendTraversable[A, C[A] <: TraversableOnce[A]](coll: C[A]): TraversableOnceExt[C[A], A] =
new TraversableOnceExt[C[A], A](coll, identity)
Which can be used like this:
可以这样使用:
val map = List(1 -> 'a', 1 -> 'à', 2 -> 'b').toMultiMap
println(map) // Map(1 -> List(a, à), 2 -> List(b))
val byFirstLetter = Set("abc", "aeiou", "cdef").toMultiMapBy(elem => (elem.head, elem))
println(byFirstLetter) // Map(c -> Set(cdef), a -> Set(abc, aeiou))
If you add the following implicit defs, it will also work with collection-like objects such as Strings and Arrays:
如果添加以下隐式 defs,它也将适用于类似集合的对象,例如Strings 和Arrays:
implicit def commomExtendStringTraversable(string: String): TraversableOnceExt[String, Char] =
new TraversableOnceExt[String, Char](string, implicitly)
implicit def commomExtendArrayTraversable[A](array: Array[A]): TraversableOnceExt[Array[A], A] =
new TraversableOnceExt[Array[A], A](array, implicitly)
Then:
然后:
val withArrays = Array(1 -> 'a', 1 -> 'à', 2 -> 'b').toMultiMap
println(withArrays) // Map(1 -> [C@377653ae, 2 -> [C@396fe0f4)
val byLowercaseCode = "Mama".toMultiMapBy(c => (c.toLower.toInt, c))
println(byLowercaseCode) // Map(97 -> aa, 109 -> Mm)
回答by Luigi Plinge
There's no method or data structure in the standard library to do this, and your solution looks about as concise as you'll get. If you use this in more than one place, you might like to factor it out into a utility method
标准库中没有方法或数据结构可以做到这一点,您的解决方案看起来尽可能简洁。如果您在多个地方使用它,您可能希望将其分解为一个实用方法
def groupTuples[A, B](seq: Seq[(A, B)]) =
seq groupBy (_._1) mapValues (_ map (_._2))
which you then obviously just call with groupTuples(seq). This might not be the most efficient possible in terms of CPU clock cycles, but I don't think it's particularly inefficient either.
然后你显然只是用groupTuples(seq). 就 CPU 时钟周期而言,这可能不是最有效的,但我认为它也不是特别低效。
I did a rough benchmark against Jean-Philippe's solution on a list of 9 tuples and this is marginally faster. Both were about twice as fast as folding the sequence into a map (effectively re-implementing groupByto give the output you want).
我在 9 个元组的列表上对 Jean-Philippe 的解决方案做了一个粗略的基准测试,这稍微快了一点。两者的速度都是将序列折叠成地图的速度的两倍(有效地重新实现groupBy以提供您想要的输出)。
回答by Johnny Everson
I don't know if you consider it cleaner:
我不知道你是否认为它更清洁:
seq.groupBy(_._1).map { case (k,v) => (k,v.map(_._2))}
回答by Xavier Guihot
Starting Scala 2.13, most collections are provided with the groupMapmethod which is (as its name suggests) an equivalent (more efficient) of a groupByfollowed by mapValues:
首先Scala 2.13,大多数集合都提供了groupMap方法,该方法(顾名思义)是 a 的等效(更有效)groupBy后跟mapValues:
List(1 -> 'a', 1 -> 'b', 2 -> 'c').groupMap(_._1)(_._2)
// Map[Int,List[Char]] = Map(2 -> List(c), 1 -> List(a, b))
This:
这:
groups elements based on the first part of tuples (Map(2 -> List((2,c)), 1 -> List((1,a), (1,b))))maps grouped values (List((1,a), (1,b))) by taking their second tuple part (List(a, b)).
groups 基于元组第一部分的元素 (Map(2 -> List((2,c)), 1 -> List((1,a), (1,b))))mapsList((1,a), (1,b))通过取第二个元组部分 (List(a, b))对值 ( ) 进行分组。

![scala 如何从列表[选项]中过滤无?](/res/img/loading.gif)