22

此代码来自 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 返回一个地图,但元素的顺序现在与以前不同。知道为什么会发生这种情况,最好的方法是避免这种情况吗?

4

2 回答 2

21

除非您专门使用 SortedMap 的子类型,否则映射(如集合)始终处于未指定的顺序。由于“groupBy”不返回 SortedMap 而只返回一般的 immutable.Map 并且也不使用 CanBuildFrom 机制,所以我认为您在这里无能为力。

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

编辑:

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

于 2013-01-21T07:35:15.490 回答
11

我在处理数据库记录时一直遇到这个问题。数据库按某个键对它们进行排序,但随后 groupBy 将其撤消!所以我开始用一个按连续相等键分组的函数来对 Sequence 类进行拉皮条:

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)
于 2013-07-16T17:45:23.777 回答