为什么Scala中的groupBy会更改列表项的顺序?

时间:2013-01-21 07:31:37

标签: scala

此代码来自Scala工作表:

case class E(a: Int, b: String)

val l = List(
    E(1, "One"),
    E(1, "Another One"),
    E(2, "Two"),
    E(2, "Another Two"),
    E(3, "Three")
)

l.groupBy(x => x.a)                             
// res11: scala.collection.immutable.Map[Int,List[com.dci.ScratchPatch.E]] =
//    Map(
//      2 -> List(E(2,Two), E(2,Another Two)),
//      1 -> List(E(1,One), E(1,Another One)),
//      3 -> List(E(3,Three))
//    )

您会注意到groupBy返回一个地图,但现在元素的顺序与之前的顺序不同。知道为什么会发生这种情况,以及避免这种情况的最佳方法是什么?

2 个答案:

答案 0 :(得分:20)

除非您专门使用SortedMap的子类型,否则地图(如集合)始终采用未指定的顺序。由于“groupBy”不返回SortedMap而只返回一般的immutable.Map,也没有使用CanBuildFrom机制,因此我认为你无法在这里做任何事情。

您可以在类似问题的答案中找到有关此主题的更多信息,例如: here

修改

如果您想将地图后贴图转换为SortedMap(按其键排序),您可以执行SortedMap(l.groupBy(_.a).toSeq:_*)import scala.collection.immutable.SortedMap)。不要...toSeq.sortWith(...).toMap,因为这不能保证生成的地图中的排序。

答案 1 :(得分:10)

我在处理数据库记录时一直遇到这种情况。数据库按一些键对它们进行排序,然后groupBy撤消它!所以我开始使用一个按连续相等键分组的函数对Sequence类进行pimping:

class PimpedSeq[A](s: Seq[A]) {

  /**
   * Group elements of the sequence that have consecutive keys that are equal.
   *
   * Use case:
   *     val lst = SQL("SELECT * FROM a LEFT JOIN b ORDER BY a.key")
   *     val grp = lst.groupConsecutiveKeys(a.getKey)
   */
  def groupConsecutiveKeys[K](f: (A) => K): Seq[(K, List[A])] = {
    this.s.foldRight(List[(K, List[A])]())((item: A, res: List[(K, List[A])]) =>
      res match {
        case Nil => List((f(item), List(item)))
        case (k, kLst) :: tail if k == f(item) => (k, item :: kLst) :: tail
        case _ => (f(item), List(item)) :: res
      })
  }
}

object PimpedSeq {
  implicit def seq2PimpedSeq[A](s: Seq[A]) = new PimpedSeq(s)
}

使用它:

import util.PimpedSeq._   // implicit conversion    
val dbRecords = db.getTheRecordsOrderedBy
val groups = dbRecords.groupConsecutiveKeys(r => r.getKey)