2013-01-21 31 views
18

此代碼是從Scala的工作表:爲什麼Scala中的groupBy會更改列表項目的排序?

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

嗨@JacobusR。與其他許多關於SO的問題相比,你的問題寫得很好,但是我希望你下次考慮在代碼格式化方面做更多的努力。如果你問我,這幾乎是完全不可讀的,而且我通常更傾向於回答一個問題,如果我不必花一分鐘「解析」代碼:) – fresskoma

+0

Hi @ x3ro,一百萬道歉!它完全放棄了我的想法。我正在忙着寫這個問題,有人走進我的辦公室,所以我剛剛發佈了它。 – Jack

+0

哦,謝謝你的編輯@ x3ro。它看起來好多了:-) – Jack

回答

19

除非您特別使用SortedMap的子類型,否則映射(如集合)總是處於未指定的順序。由於「groupBy」不會返回SortedMap,而只會返回一個不可變的常規映射,也不會使用CanBuildFrom機制,所以我認爲這裏沒有什麼可以做的。

您可以在類似問題的答案中找到關於此主題的更多信息,例如, here

編輯:

如果你想在地圖afterwarts轉換爲一個SortedMap(通過其按鍵排序),你可以做SortedMap(l.groupBy(_.a).toSeq:_*)(與import scala.collection.immutable.SortedMap)。不要做...toSeq.sortWith(...).toMap,因爲這不能保證結果圖中的排序。

+0

謝謝。要訂購地圖,我使用了l.groupBy(\ _。a).toSeq.sortWith(_._ 1 <_._ 1).toMap。不是很優雅,但嘿,這是星期一。 – Jack

+0

或'l.groupBy(_._ 1).mapValues(_。sorted).view.force' –

+1

@Peter Schmitz:這是不一樣的。您的代碼段對值進行排序,但排序問題與密鑰有關。除了通過'toMap'重新轉換回地圖的事實之外,JacobusR還是正確的。 –

8

我在處理數據庫記錄時一直遇到這個問題。數據庫通過一些密鑰對它們進行排序,但隨後groupBy將其撤銷!所以,我已經開始與一個功能,通過組連續相等鍵拉皮條的序列類:

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) 
相關問題