2013-05-18 8 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' is - how' <=> 'Результаты помогают перечислению для сортировки? как 3 значения '-1,0, + 1' помогают в перечислении для сортировки? –

+1

@Priti, это похоже на то, что имеет функция сортировки большинства языков (C, C++) - если вы хотите, чтобы первый параметр был ранжирован до второго, верните +1, если вы хотите, чтобы они отображались в том же порядке, в каком они были в список, то 0, в противном случае вы хотите, чтобы второй параметр был раньше, return -1. – Dogbert

+0

@Priti, дайте мне знать, если я неправильно понял ваши сомнения. – Dogbert

1

Это ReversedOrder Mixin может помочь вам достичь смешанные направления виды на отдельно атрибуты, используя 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

Смежные вопросы