2011-02-18 69 views
4

假設我有一個名稱列表。在Scala中擴展Seq.sortBy

case class Name(val first: String, val last: String) 

val names = Name("c", "B") :: Name("b", "a") :: Name("a", "B") :: Nil 

如果我現在想按姓氏對該列表進行排序(如果這還不夠,按名字排序),則很容易完成。

names.sortBy(n => (n.last, n.first)) 
// List[Name] = List(Name(a,B), Name(c,B), Name(b,a)) 

但是,如果我想基於字符串的其他排序規則來排序此列表?

不幸的是,下面不工作:

val o = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) } 
names.sortBy(n => (n.last, n.first))(o) 
// error: type mismatch; 
// found : java.lang.Object with Ordering[String] 
// required: Ordering[(String, String)] 
// names.sortBy(n => (n.last, n.first))(o) 

是沒有什麼辦法,讓我改變順序,而無需編寫與多個明確sortWith方法if - 爲了應對所有else分公司案件?

回答

2

一個解決方案是擴展其他隱式使用的Tuple2排序。不幸的是,這意味着在代碼中寫出Tuple2

names.sortBy(n => (n.second, n.first))(Ordering.Tuple2(o, o)) 
1

我不是100%確定你認爲collator應該有什麼方法。

但你有最大的靈活性,如果你定義的情況下,類排序:

​​

,但你也可以提供從字符串排序爲名稱排序的隱式轉換:

def ostring2oname(os: Ordering[String]) = new Ordering[Name] { 
    def compare(a: Name, b: Name) = 
    3*math.signum(os.compare(a.last,b.last)) + math.signum(os.compare(a.first,b.first)) 
} 

然後你可以用任何字符串命令來排序名稱:

def oo = new Ordering[String] { 
    def compare(x: String, y: String) = x.length compare y.length 
} 
val morenames = List("rat","fish","octopus") 

scala> morenames.sorted(oo) 
res1: List[java.lang.String] = List(rat, fish, octopus) 

編輯:一個方便的技巧,以防萬一它不明顯,是如果你想用N個東西來定購,而且你已經在使用比較,那麼你可以將每件東西乘以3^k(用first-按順序乘以3)的最大冪)並相加。


如果你的比較是非常耗時的,你可以輕鬆地添加級聯比較:

class CascadeCompare(i: Int) { 
    def tiebreak(j: => Int) = if (i!=0) i else j 
} 
implicit def break_ties(i: Int) = new CascadeCompare(i) 

然後

def ostring2oname(os: Ordering[String]) = new Ordering[Name] { 
    def compare(a: Name, b: Name) = 
    os.compare(a.last,b.last) tiebreak os.compare(a.first,b.first) 
} 

(只是要小心巢他們x tiebreak (y tiebreak (z tiebreak w)))所以你不要一連串的進行隱式轉換)。 (如果你真的需要快速比較,那麼你應該全部用手寫出來,或者將排序打包在一個數組中,並使用一個while循環,我會認爲你並不是那麼渴望性能。)

+0

嗯,我不知道。與3^k相乘看起來很聰明,但如果不遵循-1/0/+ 1慣例,它當然不會起作用。此外,它會一次評估所有比較結果,而當發現差異時,Tuple比較將停止。 – Debilski 2011-02-18 16:57:49

4

好了,這幾乎不會把戲:

names.sorted(o.on((n: Name) => n.last + n.first)) 

在另一方面,你可以這樣做,以及:

implicit val o = new Ordering[String]{ def compare(x: String, y: String) = collator.compare(x, y) } 
names.sortBy(n => (n.last, n.first)) 

這本地定義隱含的優先級高於定義的在Ordering對象上。