Comment puis-je trier par plusieurs conditions avec différentes commandes?
Question
Je voudrais vraiment pouvoir gérer ça sans utiliser la technique du singe, mais je n'ai pas encore trouvé d'autre option.
J'ai un tableau (en Ruby) que je dois 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 par ordre croissant et de la seconde pour trier par ordre décroissant. Par exemple:
ordered_list = [[1, 2], [1, 1], [2, 1]]
Des suggestions?
Edit: Je viens de me rendre compte que je devrais mentionner que je ne peux pas facilement comparer les première et seconde valeurs (je travaille actuellement avec des attributs d’objet). Donc, pour un exemple simple, cela ressemble plus à:
ordered_list = [[1, "b"], [1, "a"], [2, "a"]]
La solution
Que diriez-vous de:
ordered_list = [[1, "b"], [1, "a"], [2, "a"]]
ordered_list.sort! do |a,b|
[a[0],b[1]] <=> [b[0], a[1]]
end
Autres conseils
Je passais mon temps à cauchemarder pour essayer de trouver comment trier de manière inverse un attribut spécifique, mais normalement les deux autres. Juste une note sur le tri pour ceux qui viennent après cela et qui sont déroutés 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
. De même, comme indiqué précédemment par les autres afficheurs, permutez a
et b
sur l'opérateur de comparaison <=>
pour inverser l'ordre de tri. Comme ceci:
Pour trier par blah et craw normalement, mais par bleu dans l'ordre inverse, procédez comme suit:
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 a.craw
ou <=> pour effectuer un tri inversé sur les chiffres (pour autant que je sache, cela ne fonctionne que sur les chiffres, n’essayez donc pas avec des chaînes c'est juste des erreurs et tue la page).
Supposons que <=> est un entier. Par exemple:
something.sort_by!{|a| [a.blah, -a.craw, a.bleu]}
J'ai eu le même problème de base et je l'ai 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 le < = > fonction, qui vous permet ensuite de faire des choses comme ceci:
your_objects.sort_by {|y| [y.prop1,Inverter.new(y.prop2)]}
Enumerable#multisort
est une solution générique qui peut être appliquée à des tableaux de toute taille , pas uniquement à ceux comportant 2 éléments. Les arguments sont des 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"]]
J'utilise la recette de Glenn depuis un bon moment maintenant. Fatigué de copier le code d'un projet à l'autre, j'ai décidé d'en faire un joyau: