列表、地图等中的groupBy方法在函数之后生成一个地图。
有没有一种方法可以使用groupBy生成保持插入顺序的映射(例如LinkedHashMap)?
我使用for循环来手动插入,但我想知道是否有一个有用的已定义函数可以帮助我。
提前谢谢。
发布于 2012-03-07 11:30:05
TraversableLike上定义的groupBy会生成一个immutable.Map,所以您不能让此方法生成其他内容。
已经保留了每个条目中元素的顺序,但没有保留键的顺序。键是所提供函数的结果,因此它们实际上没有顺序。
如果你想根据一个特定的键第一次出现的情况下订单,这里有一个你可以做的草图。假设我们想按整数的值/2对它们进行分组:
val m = List(4, 0, 5, 1, 2, 6, 3).zipWithIndex groupBy (_._1 / 2)
val lhm = LinkedHashMap(m.toSeq sortBy (_._2.head._2): _*)
lhm mapValues (_ map (_._1))
// Map(2 -> List(4, 5), 0 -> List(0, 1), 1 -> List(2, 3), 3 -> List(6))
// Note order of keys is same as first occurrence in original list发布于 2012-03-08 04:47:20
下面的代码将为您提供一个groupByOrderedUnique方法,该方法的行为符合您的要求。它还添加了一个groupByOrdered,可以像其他人在评论中所要求的那样保留副本。
import collection.immutable.ListSet
import collection.mutable.{LinkedHashMap => MMap, Builder}
implicit class GroupByOrderedImplicitImpl[A](val t: Traversable[A]) extends AnyVal {
def groupByOrderedUnique[K](f: A => K): Map[K, ListSet[A]] =
groupByGen(ListSet.newBuilder[A])(f)
def groupByOrdered[K](f: A => K): Map[K, List[A]] =
groupByGen(List.newBuilder[A])(f)
def groupByGen[K, C[_]](makeBuilder: => Builder[A, C[A]])(f: A => K): Map[K, C[A]] = {
val map = MMap[K, Builder[A, C[A]]]()
for (i <- t) {
val key = f(i)
val builder = map.get(key) match {
case Some(existing) => existing
case None =>
val newBuilder = makeBuilder
map(key) = newBuilder
newBuilder
}
builder += i
}
map.mapValues(_.result).toMap
}
}当我使用如下代码时:
import GroupByOrderedImplicit._
val range = 0.until(40)
val in = range ++ range.reverse
println("With dupes:")
in.groupByOrdered(_ % 10).toList.sortBy(_._1).foreach(println)
println("\nUnique:")
in.groupByOrderedUnique(_ % 10).toList.sortBy(_._1).foreach(println)我得到以下输出:
With dupes:
(0,List(0, 10, 20, 30, 30, 20, 10, 0))
(1,List(1, 11, 21, 31, 31, 21, 11, 1))
(2,List(2, 12, 22, 32, 32, 22, 12, 2))
(3,List(3, 13, 23, 33, 33, 23, 13, 3))
(4,List(4, 14, 24, 34, 34, 24, 14, 4))
(5,List(5, 15, 25, 35, 35, 25, 15, 5))
(6,List(6, 16, 26, 36, 36, 26, 16, 6))
(7,List(7, 17, 27, 37, 37, 27, 17, 7))
(8,List(8, 18, 28, 38, 38, 28, 18, 8))
(9,List(9, 19, 29, 39, 39, 29, 19, 9))
Unique:
(0,ListSet(0, 10, 20, 30))
(1,ListSet(1, 11, 21, 31))
(2,ListSet(2, 12, 22, 32))
(3,ListSet(3, 13, 23, 33))
(4,ListSet(4, 14, 24, 34))
(5,ListSet(5, 15, 25, 35))
(6,ListSet(6, 16, 26, 36))
(7,ListSet(7, 17, 27, 37))
(8,ListSet(8, 18, 28, 38))
(9,ListSet(9, 19, 29, 39))发布于 2015-03-26 05:44:51
这里有一个没有地图的例子:
def orderedGroupBy[T, P](seq: Traversable[T])(f: T => P): Seq[(P, Traversable[T])] = {
@tailrec
def accumulator(seq: Traversable[T], f: T => P, res: List[(P, Traversable[T])]): Seq[(P, Traversable[T])] = seq.headOption match {
case None => res.reverse
case Some(h) => {
val key = f(h)
val subseq = seq.takeWhile(f(_) == key)
accumulator(seq.drop(subseq.size), f, (key -> subseq) :: res)
}
}
accumulator(seq, f, Nil)
}如果您只需要按顺序访问结果(不需要随机访问),并且希望避免创建和使用Map对象的开销,那么它可能很有用。注意:我没有将性能与其他选项进行比较,它实际上可能更差。
EDIT:为了清楚起见,这里假设您的输入已经按组键排序。我的用例是SELECT ... ORDER BY。
https://stackoverflow.com/questions/9594431
复制相似问题