2008-09-16 18 views
18

Ich würde wirklich gerne ohne Affe-Patching damit umgehen, aber ich konnte noch keine andere Option finden.Wie kann ich nach verschiedenen Bedingungen mit verschiedenen Bestellungen sortieren?

Ich habe ein Array (in Ruby), dass ich nach mehreren Bedingungen sortieren muss. Ich weiß, wie man die Sortiermethode verwendet, und ich habe den Trick beim Sortieren mit einem Array von Optionen verwendet, um nach mehreren Bedingungen zu sortieren. In diesem Fall brauche ich jedoch die erste Bedingung, um aufsteigend zu sortieren, und die zweite, um absteigend zu sortieren. Zum Beispiel:

ordered_list = [[1, 2], [1, 1], [2, 1]] 

Irgendwelche Vorschläge?

Edit: Gerade realisierte ich sollte erwähnen, dass ich den ersten und zweiten Wert nicht leicht vergleichen kann (ich arbeite tatsächlich mit Objektattributen hier). So für ein einfaches Beispiel ist es mehr wie:

ordered_list = [[1, "b"], [1, "a"], [2, "a"]] 
+0

Ihr modifiziertes Beispiel kann genauso behandelt werden wie das erste, das Sie gepostet haben. Der Operator <=> funktioniert auf jedem Objekt auf die gleiche Weise (in Ihrem Fall können Integer und String-Objekte beide mit <=> verglichen werden) –

+0

Richtig, nur gedacht, ich sollte es erwähnen, anstatt das Problem zu vereinfachen. –

Antwort

32

Wie wäre:

 

ordered_list = [[1, "b"], [1, "a"], [2, "a"]] 
ordered_list.sort! do |a,b| 
    [a[0],b[1]] <=> [b[0], a[1]] 
end 
 
+0

Wunderbar! Hätte daran gedacht, wusste, dass ich etwas verpasst habe! Vielen Dank! –

+1

Das ist RAD! Zeit, das in meinem Rubintricks-Buch abzulegen! Kudos! –

+4

Ich finde 'a [0] <=> b [0] oder b [1] <=> a [1]' ein bisschen besser lesbar. – maasha

4

ich das gleiche Grundproblem hatte, und löste es dies, indem:

class Inverter 
    attr_reader :o 

    def initialize(o) 
    @o = o 
    end 

    def <=>(other) 
    if @o.is && other.o.is 
     -(@o <=> other.o) 
    else 
     @o <=> other.o 
    end 
    end 
end 

Dies ist ein Wrapper, der einfach die < => Funktion umkehrt, die Ihnen dann erlaubt, Dinge wie diese zu tun:

your_objects.sort_by {|y| [y.prop1,Inverter.new(y.prop2)]} 
4

Enumerable#multisort ist eine generische Lösung, die auf Arrays von jeder Größe angewendet werden kann, nicht nur diejenigen mit 2 Elementen. Die Argumente sind booleans, ob ein bestimmtes Feld angeben sollte auf- oder absteigend (usage unten) sortiert werden:

items = [ 
    [3, "Britney"], 
    [1, "Corin"], 
    [2, "Cody"], 
    [5, "Adam"], 
    [1, "Sally"], 
    [2, "Zack"], 
    [5, "Betty"] 
] 

module Enumerable 
    def multisort(*args) 
    sort do |a, b| 
     i, res = -1, 0 
     res = a[i] <=> b[i] until !res.zero? or (i+=1) == a.size 
     args[i] == false ? -res : res 
    end 
    end 
end 

items.multisort(true, false) 
# => [[1, "Sally"], [1, "Corin"], [2, "Zack"], [2, "Cody"], [3, "Britney"], [5, "Betty"], [5, "Adam"]] 
items.multisort(false, true) 
# => [[5, "Adam"], [5, "Betty"], [3, "Britney"], [2, "Cody"], [2, "Zack"], [1, "Corin"], [1, "Sally"]] 
+0

Ordentlich! Danke, das wird dir später sicher nützlich sein. –

+0

Wow - die Single-Line-Schleife würde 99% der Rubinisten testen. Sehr kompakt. – drudru

2

Ich habe jetzt schon eine ganze Weile Glenn Rezept im Einsatz. Müde des Kopierens Code von Projekt Projekt über und immer wieder, habe ich beschlossen, es ein Juwel zu machen:

http://github.com/dadooda/invert

+0

Hey, das ist sehr cool! –

7

Ich habe versucht, einen Alptraum von einer Zeit, die um herauszufinden, wie Art eine bestimmte umkehren Attribut, aber sortieren Sie normalerweise die anderen zwei. Nur eine Anmerkung über die Sortierung für diejenigen, die danach kommen und durch die | a, b | verwirrt sind Block-Syntax. Sie können den Blockstil {|a,b| a.blah <=> b.blah} nicht mit sort_by! oder sort_by verwenden. Es muss mit sort! oder sort verwendet werden. Wie zuvor von den anderen Postern angezeigt, tauschen Sie auch a und b über den Vergleichsoperator <=> aus, um die Sortierreihenfolge umzukehren. Wie folgt aus:

Um von blah und craw zu sortieren normalerweise, aber sortieren nach bleu in umgekehrter Reihenfolge wie folgt vorgehen:

something.sort!{|a,b| [a.blah, b.bleu, a.craw] <=> [b.blah, a.bleu, b.craw]} 

Es ist auch möglich, das - Schild mit sort_by oder sort_by! zu verwenden, um eine umgekehrte Art zu tun auf Zahlen (soweit mir bekannt ist, funktioniert es nur auf Zahlen, also versuche es nicht mit Strings, da es nur Fehler und die Seite tötet).

Angenommen a.craw ist eine ganze Zahl.Zum Beispiel:

something.sort_by!{|a| [a.blah, -a.craw, a.bleu]} 
Verwandte Themen