2013-05-18 24 views
11

Próbuję posortować tablicę obiektów na podstawie różnych atrybutów. Niektóre z tych atrybutów chciałbym posortować w porządku rosnącym, a niektóre w porządku malejącym. Mogłem sortować rosnąco lub zstępując, ale nie udało mi się połączyć tych dwóch elementów.Sortowanie wielu wartości według rosnącej i malejącej wartości

Oto proste klasy pracuję z:

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 

mogę wtedy instancji niektóre psy mają być sortowane później.

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

Mogę następnie użyć metody sort_all_by_gender_then_name.

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

Tablica, którą zwraca, obejmuje najpierw kobiety, a następnie mężczyzn, wszystkie posortowane według nazwy w kolejności rosnącej.

Ale co, jeśli chcę, aby płeć zstępowała, a następnie nazwa rosnąco, tak aby najpierw były to mężczyźni, a następnie posortowane według nazwy rosnąco. W tym przypadku:

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

Albo gdybym chciał go przez rosnącym płci, ale nazwa malejąco:

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

Przy sortowaniu wartości liczbowe można poprzedzić a - zrobić to w odwrotnej kolejności sortowania. Jednak nie byłem w stanie znaleźć sposób, aby to zrobić z ciągów. Każda pomoc lub pomysły będą mile widziane. Dzięki.

Odpowiedz

13

Oto jeden ze sposobów, aby to zrobić za pomocą .sort zamiast .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) 

wyjściowa:

[{: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"}] 

Zasadniczo, to robi coś podobnego do negując numerów (jak wspomniano w pytaniu) , zamieniając właściwość na inny element, jeśli trzeba go posortować w porządku malejącym.

+0

Mam wątpliwości, że nadal nie rozumiem o 'enum # sort' is - jak' <=> 'wyniki pomagają enum się posortować? w jaki sposób 3 wartości "-1,0, + 1" pomagają w wyliczaniu enum? –

+1

@Priti, jest podobny do funkcji sortowania języka (C, C++) - jeśli chcesz, aby pierwszy parametr był oceniany przed drugim, zwróć +1, jeśli chcesz, aby pojawiały się w tej samej kolejności, w jakiej były listę, a następnie 0, w przeciwnym razie drugi parametr ma być przed, return -1. – Dogbert

+0

@Priti, daj mi znać, jeśli źle zrozumiałem twoje wątpliwości. – Dogbert

1

Ten ReversedOrder mixin może pomóc osiągnąć mieszane rodzaje kierunkowych na oddzielne atrybuty, używając sort_by:

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

Przykład zastosowania:

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"}] 

Uwaga: Należy uważać, aby dup, element odwrócone. Bez tego zmiksujesz inwerter porównawczy z rzeczywistym obiektem, zamiast tylko kluczem wykonanym dla sort_by i na zawsze będzie on wykonywał odwrotne porównania.

+0

Czy wydajność nie będzie naprawdę zła z powodu wielokrotnego "rozszerzenia"? –

+0

Tak, będą (pamięć podręczna). – chikamichi

Powiązane problemy