Подтвердить что ты не робот

Почему groupBy в 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 возвращает карту, но упорядочение элементов теперь отличается от способа, которым они были раньше. Любая идея, почему это происходит, и что лучший способ избежать этого?

4b9b3361

Ответ 1

Если вы специально не используете подтип SortedMap, карта (например, набор) всегда находится в неуказанном порядке. Поскольку "groupBy" не возвращает SortedMap, а только общую неизменяемую. Map, а также не использует механизм CanBuildFrom, я думаю, что вы ничего не можете здесь сделать.

Вы можете найти больше на эту тему в ответах на подобные вопросы, например. здесь.

Edit:

Если вы хотите преобразовать карты после вызова в SortedMap (упорядоченные по его клавишам), вы можете сделать SortedMap(l.groupBy(_.a).toSeq:_*) (с помощью import scala.collection.immutable.SortedMap). Не выполняйте ...toSeq.sortWith(...).toMap, потому что это не гарантирует упорядочение в полученной карте.

Ответ 2

Я сталкиваюсь с этим все время при работе с записями базы данных. База данных сортирует их по какому-то ключу, но затем 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)