2012-03-19 21 views
1

我拼命試圖找到一種排序字符串列表的方法,其中字符串是以下形式的預定義標識符:a1.1,a1.2,..., a1.100,a2.1,a2.2,...,a2.100,...,b1.1,b1.2等等,這是正確的順序。因此,每個標識符首先按其第一個字符排序(按字母順序降序排列),然後在此排序中按連續數字排序。我已經嘗試sortWith通過提供一個排序函數爲所有兩個連續列表成員指定上述規則。根據Scala中的多個約束排序列表

scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => a.take(1) < b.take(1) && a.drop(1).toDouble < b.drop(1).toDouble) 
res2: List[java.lang.String] = List(a1.102, a1.1, b2.2, b2.1) 

這不是我預期的順序。然而,通過交換表達式的排序,因爲

scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => (a.drop(1).toDouble < b.drop(1).toDouble && a.take(1) < b.take(2))) 
res3: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2) 

這確實給了我(至少在這個例子中)所需的排序,這是我不明白也沒有。

我會很感激,如果有人可以給我一個提示那裏究竟發生了什麼,以及如何按照我的意願對列表進行排序(使用更復雜的布爾表達式,而不僅僅是比較<或>)。還有一個問題:我排序的字符串(在我的例子中)實際上是來自HashMap m的鍵。任何解決方案的效果將排序按其鍵

m.toSeq.sortWith((a: (String, String), b: (String, String)) => a._1.drop(1).toDouble < b._1.drop(1).toDouble && a._1.take(1) < b._1.take(1)) 

非常感謝先進!

+0

非常感謝大家!我是Scala編程新手,但我非常喜歡它!但是我仍然遇到了一些令人頭痛的問題,比如這些;)我假設,我會找回一個列表,每個連續兩個元素都與我的約束匹配。我已經接受了第二個答案,因爲我現在對我來說都很清楚。由於每一個答案都能幫助我學到更多東西,但我對這個董事會是新手,請讓我知道,如果需要的話,我可以怎樣批准那些有用的答案!謝謝你的友好:)乾杯! – 2012-03-19 20:37:40

回答

6

更新:我誤讀了你的例子 - 你想a1.2a1.102之前,其中toDouble版本將不會正確。我建議不要使用以下:

items.sortBy { s => 
    val Array(x, y) = s.tail.split('.') 
    (s.head, x.toInt, y.toInt) 
} 

這裏我們使用Scala的Ordering實例Tuple3[Char, Int, Int]


它看起來像你有你的第二個(「正確」)的版本一個錯字:b.take(2)應該是沒有意義的,而應該是b.take(1)相匹配的第一個。一旦你解決了這個問題,你會得到相同的(不正確的)順序。

真正的問題是,你只需要在數字匹配的情況下的第二個條件。所以下面的作品期望:

val items = List("a1.102", "b2.2", "b2.1", "a1.1") 
items.sortWith((a, b) => 
    a.head < b.head || (a.head == b.head && a.tail.toDouble < b.tail.toDouble) 
) 

其實我建議以下,但:

items.sortBy(s => s.head -> s.tail.toDouble) 

在這裏,我們利用這一斯卡拉Tuple2[Char, Double]提供適當的Ordering實例的事實優勢,所以我們可以提供一個轉換功能,將您的項目轉換爲該類型。

並回答您的最後一個問題:是的,這些方法的任何一個都可以在您的Map示例中正常工作。

0

因此,考慮當您的排序功能通過a="a1.1"b="a1.102"會發生什麼。 你想要的是讓函數返回true。但是,a.take(1) < b.take(1)返回false,所以函數返回false。

想想你的情況下,多一點謹慎

  • 如果前綴是平等的,尾部是有序的正確,那麼參數都正確有序
  • 如果前綴不相等,則該參數只有在前綴是的情況下才能正確排序。

那麼試試這個來代替:

(a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1) 

這返回正確的順序:

scala> List("a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => if (a.take(1) == b.take(1)) a.drop(1).toDouble < b.drop(1).toDouble else a.take(1) < b.take(1)) 
res8: List[java.lang.String] = List(a1.1, a1.102, b2.1, b2.2) 

它在顛倒順序爲你工作的原因是運氣。考慮額外的輸入"c0",看看發生了什麼事:

scala> List("c0", "a1.102", "b2.2", "b2.1", "a1.1").sortWith((a: String, b: String) => (a.drop(1).toDouble < b.drop(1).toDouble && a.take(1) < b.take(2))) 
res1: List[java.lang.String] = List(c0, a1.1, a1.102, b2.1, b2.2) 

,轉回的功能種類的字符串的數字部分第一,然後在前綴。恰好你所給的數字順序也保留了前綴順序,但事實並非總是如此。

0

創建一個元組,其中包含"."之前的字符串,然後是"."之後的整數。這將使用第一部分的字典順序和第二部分的整數順序。

scala> val order = Ordering.by((s:String) => (s.split("\\.")(0),s.split("\\.")(1).toInt)) 
order: scala.math.Ordering[String] = [email protected] 

scala> res2 
res8: List[java.lang.String] = List(a1.5, a2.2, b1.11, b1.8, a1.10) 


scala> res2.sorted(order) 
res7: List[java.lang.String] = List(a1.5, a1.10, a2.2, b1.8, b1.11) 
+0

沒關係,前綴只能達到a2,但如果達到a10或更高,字典順序將停止工作,所以可能更安全,以便像Travis那樣做 – 2012-03-19 21:45:57