2008-09-16 9 views
18

Je voudrais vraiment gérer cela sans patch-singe mais je n'ai pas été en mesure de trouver une autre option pour le moment.Comment puis-je trier selon plusieurs conditions avec différentes commandes?

J'ai un tableau (en Ruby) que j'ai besoin de trier selon plusieurs conditions. Je sais comment utiliser la méthode de tri et j'ai utilisé l'astuce sur le tri en utilisant un tableau d'options pour trier selon plusieurs conditions. Cependant, dans ce cas j'ai besoin de la première condition pour trier ascendant et la seconde pour trier décroissant. Par exemple:

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

Des suggestions? Edit: Je viens de réaliser que je dois mentionner que je ne peux pas facilement comparer les première et deuxième valeurs (je travaille en fait avec des attributs d'objet ici). Donc, pour un exemple simple, il est plus comme:

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

votre exemple modifié peut être traité de la même manière que le premier que vous avez publié. L'opérateur <=> travaillera sur n'importe quel objet de la même manière (dans votre cas, les objets Integer et String peuvent tous deux être comparés avec <=> très bien) –

+0

Vrai, je pensais juste que je devrais le mentionner plutôt que de risquer de simplifier le problème. –

Répondre

32

Que diriez-vous:

 

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

Merveilleux! J'aurais dû y penser, je savais qu'il me manquait quelque chose! Merci! –

+1

C'est RAD! Il est temps de classer ça dans mon livre de trucs rubis! Gloire! –

+4

Je trouve 'a [0] <=> b [0] ou b [1] <=> a [1]' un peu plus lisible. – maasha

4

Je l'ai eu ce même problème fondamental, et résolu en ajoutant ceci:

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 

C'est un wrapper qui inverse simplement la fonction < =>, ce qui vous permet ensuite de faire des choses comme ceci:

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

Enumerable#multisort est une solution générique qui peut être appliquée à des tableaux de n'importe quelle taille, pas seulement ceux avec 2 éléments. Les arguments sont booléens qui indiquent si un champ spécifique doit être trié par ordre croissant ou décroissant (utilisation ci-dessous):

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

Neat! Merci, ça sera certainement utile plus tard. –

+0

Wow - la boucle à une seule ligne testerait 99% des rubis. Très compact – drudru

2

Je l'ai utilisé la recette de Glenn pour un certain temps maintenant. Fatigué de copier le code d'un projet à plusieurs reprises, je l'ai décidé de faire un petit bijou:

http://github.com/dadooda/invert

+0

Hé, c'est très cool! –

7

je faisais un cauchemar d'un temps à essayer de comprendre comment inverser une sorte spécifique attribut mais normalement trier les deux autres. Juste une note sur le tri pour ceux qui viennent après cela et sont confus par le | a, b | syntaxe de bloc. Vous ne pouvez pas utiliser le style de bloc {|a,b| a.blah <=> b.blah} avec sort_by! ou sort_by. Il doit être utilisé avec sort! ou sort. En outre, comme indiqué précédemment par les autres affiches échanger a et b à travers l'opérateur de comparaison <=> pour inverser l'ordre de tri. Comme ceci:

Pour trier par blah et Craw normalement, mais Tri bleu dans l'ordre inverse faire ceci:

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

Il est également possible d'utiliser le signe - avec sort_by ou sort_by! faire une sorte inverse sur les chiffres (pour autant que je sache, cela ne marche que sur les nombres, donc ne l'essayez pas avec des cordes car ce sont juste des erreurs et ça tue la page).

Supposons que a.craw est un nombre entier.Par exemple:

something.sort_by!{|a| [a.blah, -a.craw, a.bleu]} 
Questions connexes