2013-05-18 80 views
11

我想根據不同的屬性對對象數組進行排序。其中一些屬性我想按升序排列,有些則按降序排列。我已經能夠按升序或降序排序,但無法將兩者結合起來。通過升序和降序排序多個值

下面是簡單的類我一起工作:

class Dog 
    attr_reader :name, :gender 

    DOGS = [] 

    def initialize(name, gender) 
    @name = name 
    @gender = gender 
    DOGS << self 
    end 

    def self.all 
    DOGS 
    end 

    def self.sort_all_by_gender_then_name 
    self.all.sort_by { |d| [d.gender, d.name] } 
    end 
end 

然後我就可以實例化一些狗稍後進行排序。

@rover = Dog.new("Rover", "Male") 
@max = Dog.new("Max", "Male") 
@fluffy = Dog.new("Fluffy", "Female") 
@cocoa = Dog.new("Cocoa", "Female") 

然後我可以使用sort_all_by_gender_then_name方法。

Dog.sort_all_by_gender_then_name 
=> [@cocoa, @fluffy, @max, @rover] 

返回的數組首先包括女性,然後是男性,所有女性都按名稱按升序排序。

但是,如果我想讓性別降序,然後命名升序,那麼它將首先是男性,然後按名稱升序排序。在這種情況下:

=> [@max, @rover, @cocoa, @fluffy] 

或者,如果我想它的性別上升,但名字下降:

=> [@fluffy, @cocoa, @rover, @max] 

排序時的數值,可以在前面加上一個 - 使它排序相反。但是,我一直無法找到一種方法來處理字符串。任何幫助或想法,將不勝感激。謝謝。

回答

13

下面是一個使用到做這件事的.sort代替.sort_by

dogs = [ 
    { name: "Rover", gender: "Male" }, 
    { name: "Max", gender: "Male" }, 
    { name: "Fluffy", gender: "Female" }, 
    { name: "Cocoa", gender: "Female" } 
] 

# gender asc, name asc 
p(dogs.sort do |a, b| 
    [a[:gender], a[:name]] <=> [b[:gender], b[:name]] 
end) 

# gender desc, name asc 
p(dogs.sort do |a, b| 
    [b[:gender], a[:name]] <=> [a[:gender], b[:name]] 
end) 

# gender asc, name desc 
p(dogs.sort do |a, b| 
    [a[:gender], b[:name]] <=> [b[:gender], a[:name]] 
end) 

輸出:

[{:name=>"Cocoa", :gender=>"Female"}, {:name=>"Fluffy", :gender=>"Female"}, {:name=>"Max", :gender=>"Male"}, {:name=>"Rover", :gender=>"Male"}] 
[{:name=>"Max", :gender=>"Male"}, {:name=>"Rover", :gender=>"Male"}, {:name=>"Cocoa", :gender=>"Female"}, {:name=>"Fluffy", :gender=>"Female"}] 
[{:name=>"Fluffy", :gender=>"Female"}, {:name=>"Cocoa", :gender=>"Female"}, {:name=>"Rover", :gender=>"Male"}, {:name=>"Max", :gender=>"Male"}] 

基本上,這是做類似否定的數字(如你在問題中提到的)東西如果需要按降序對屬性進行排序,則將屬性交換到其他元素。

+0

我有一個疑問,我仍然不明白'enum#sort'是 - <=>'結果如何幫助枚舉進行排序? 3值「-1,0,+ 1」如何幫助枚舉進行排序? –

+1

@Priti,它與大多數語言的(C,C++)排序函數具有相似性 - 如果您希望第一個參數在第二個參數之前進行排序,則返回+1,如果您希望它們以與它們相同的順序顯示該列表,然後是0,否則你希望第二個參數在之前返回-1。 – Dogbert

+0

@Priti,如果我誤解了你的疑問,請告訴我。 – Dogbert

1

ReversedOrder混入可以幫助你實現對單獨的屬性混合的方向排序,使用sort_by

module ReversedOrder 
    def <=>(other) 
    - super 
    end 
end 

使用例如:

dogs = [ 
    { name: "Rover", gender: "Male" }, 
    { name: "Max", gender: "Male" }, 
    { name: "Fluffy", gender: "Female" }, 
    { name: "Cocoa", gender: "Female" } 
] 

dogs.sort_by {|e| [e[:gender], e[:name]] } 
=> [{:name=>"Cocoa", :gender=>"Female"}, 
{:name=>"Fluffy", :gender=>"Female"}, 
{:name=>"Max", :gender=>"Male"}, 
{:name=>"Rover", :gender=>"Male"}] 

dogs.sort_by {|e| [e[:gender].dup.extend(ReversedOrder), e[:name]] } 
=> [{:name=>"Max", :gender=>"Male"}, 
{:name=>"Rover", :gender=>"Male"}, 
{:name=>"Cocoa", :gender=>"Female"}, 
{:name=>"Fluffy", :gender=>"Female"}] 

注意:小心dup反轉元素。如果沒有這些,你會將比較逆變器與實際物體混合,而不是僅僅爲sort_by生成密鑰,並且它將永遠產生反向比較。

+0

由於多個'extend',性能會不會很差? –

+0

是的,他們會(緩存清除)。 – chikamichi