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