首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >Scala GroupBy保持插入顺序?

Scala GroupBy保持插入顺序?
EN

Stack Overflow用户
提问于 2012-03-07 09:13:57
回答 4查看 11.9K关注 0票数 31

列表、地图等中的groupBy方法在函数之后生成一个地图。

有没有一种方法可以使用groupBy生成保持插入顺序的映射(例如LinkedHashMap)?

我使用for循环来手动插入,但我想知道是否有一个有用的已定义函数可以帮助我。

提前谢谢。

EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2012-03-07 11:30:05

TraversableLike上定义的groupBy会生成一个immutable.Map,所以您不能让此方法生成其他内容。

已经保留了每个条目中元素的顺序,但没有保留键的顺序。键是所提供函数的结果,因此它们实际上没有顺序。

如果你想根据一个特定的键第一次出现的情况下订单,这里有一个你可以做的草图。假设我们想按整数的值/2对它们进行分组:

代码语言:javascript
运行
复制
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
票数 24
EN

Stack Overflow用户

发布于 2012-03-08 04:47:20

下面的代码将为您提供一个groupByOrderedUnique方法,该方法的行为符合您的要求。它还添加了一个groupByOrdered,可以像其他人在评论中所要求的那样保留副本。

代码语言:javascript
运行
复制
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
  }
}

当我使用如下代码时:

代码语言:javascript
运行
复制
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)

我得到以下输出:

代码语言:javascript
运行
复制
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))
票数 21
EN

Stack Overflow用户

发布于 2015-03-26 05:44:51

这里有一个没有地图的例子:

代码语言:javascript
运行
复制
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

票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/9594431

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档