8

我有帖子對象的集合,我希望能夠基於這些條件對它們進行排序:排序由多個條件在Ruby中

  • 首先,按類別(新聞,事件,實驗室,組合報告等內容。)
  • 然後按日期,如果日期,或位置,如果一個特定的指數定爲它

有些職位有日期(新聞事件),其他人將有明確的職務(實驗室,和投資組合)。

我想打電話給posts.sort!,所以我重寫了<=>,但我正在尋找這些條件排序的最有效方法。下面是一個僞方法:

def <=>(other) 
    # first, everything is sorted into 
    # smaller chunks by category 
    self.category <=> other.category 

    # then, per category, by date or position 
    if self.date and other.date 
    self.date <=> other.date 
    else 
    self.position <=> other.position 
    end 
end 

好像我不得不實際上排序兩個獨立的時代,而不是填鴨式的一切到一個方法。像sort_by_category,然後sort!。什麼是最寶貴的方式來做到這一點?

回答

12

您應該始終按照相同的標準排序以確保有意義的順序。如果比較兩個nil日期,則position將判斷訂單是好的,但如果將一個nil日期與一個設定日期進行比較,則必須決定哪一個先到達,而不考慮該位置(例如通過將nil映射到一天過去的方式)。

否則設想以下:

a.date = nil     ; a.position = 1 
b.date = Time.now - 1.day  ; b.position = 2 
c.date = Time.now    ; c.position = 0 

通過原來的標準,你會:一個< b <Ç<一個。那麼,哪一個最小?

您也想一次完成排序。爲了您的<=>實現,使用#nonzero?

def <=>(other) 
    return nil unless other.is_a?(Post) 
    (self.category <=> other.category).nonzero? || 
    ((self.date || AGES_AGO) <=> (other.date || AGES_AGO)).nonzero? || 
    (self.position <=> other.position).nonzero? || 
    0 
end 

如果您使用比較標準只有一次,或者如果該標準並不具有普遍性,因此不希望定義<=>,你可以使用sort有塊:

post_ary.sort{|a, b| (a.category <=> ...).non_zero? || ... } 

更妙的是,有sort_bysort_by!,你可以用它來構建什麼比較數組中的優先級:

post_ary.sort_by{|a| [a.category, a.date || AGES_AGO, a.position] } 

除了縮短之外,使用sort_by還有一個優點,即您只能獲得排序良好的標準。

注:

  • sort_by!用Ruby 1.9.2中引入的。您可以require 'backports/1.9.2/array/sort_by'與較老的紅寶石一起使用。
  • 我假設Post不是ActiveRecord::Base的子類(在這種情況下,您希望排序由db服務器完成)。
+0

謝謝,我不知道的'數字#非零?'。 '''方法返回非布爾值是不是有點奇怪? – 2010-04-14 06:58:16

+0

@Mladen:這是,但非常有用。另一個你可能期望得到'true/false'的例子:'String 2010-04-14 13:53:36

+0

這是有點誤導: 'post_ary.sort_by {| a,b | (a.category <=> ...)}' sort_by不帶有兩個參數的塊。對於更復雜的排序問題,您應該返回一個數組。 [a.category,a.date,a.position]}' – Timo 2012-03-27 12:50:05

3

或者,您可以在數組中一舉排序,唯一的問題是處理其中一個屬性爲零的情況,但如果您知道數據集是通過選擇適當的防守。如果日期和位置比較以優先順序或一個或另一個列出(即使用日期,如果兩個其他使用位置均存在),則從僞代碼中不清楚。第一個解決方案假設使用,類別,其次是日期,其次是位置

def <=>(other) 
    [self.category, self.date, self.position] <=> [other.category, other.date, other.position] 
end 

其次假定它是日期或位置

def <=>(other) 
    if self.date && other.date 
     [self.category, self.date] <=> [other.category, other.date] 
    else 
     [self.category, self.position] <=> [other.category, other.position] 
    end 
end 
+0

啊,忘記了日期的'零'。這種排序順序沒有很好的順序(見我更新的答案)。 – 2010-04-14 13:51:31

+0

對於我的學習,你的意思是沒有被妥善安排? – naven87 2010-04-14 15:35:24

+2

對於一個有根據的命令,以下總是成立:'a 2010-04-14 18:32:37