Trier un tableau d'objets dans Ruby par attribut d'objet?

129

J'ai un tableau d'objets dans Ruby on Rails. Je veux trier le tableau par un attribut de l'objet. C'est possible?

l'homme d'étain
la source

Réponses:

223

Je recommande d'utiliser sort_by à la place:

objects.sort_by {|obj| obj.attribute}

Surtout si l'attribut peut être calculé.

Ou une approche plus concise:

objects.sort_by(&:attribute)
Scott
la source
81
Ou la version abrégée:objects.sort_by(&:attribute)
Nikola
3
objets encore plus courts.sort_by &: attribut
jasin_89
1
Si vous rencontrez des problèmes avec les lettres majuscules et minuscules triées séparément, vous pouvez utiliserobjects.sort_by { |obj| obj.attribute.downcase }
campeterson
1
Avez-vous une idée de la comparaison avec sort!(par exemple, la vitesse, etc.)?
Joshua Pinter du
Enumerable # sort_by docs
Matt R. Wilson
35

Oui, Array#sort!il est facile de l'utiliser.

myarray.sort! { |a, b|  a.attribute <=> b.attribute }
Konrad Rudolph
la source
Thnx buddy mais cela n'a pas fonctionné pour moi, j'ai un éventail d'objets. Dans lequel l'un des attributs de l'objet est créé_at. Je veux le trier avec ce champ. alors j'ai fait @ comm_bytes.sort! {| a, b | a.created_at <=> b.created_at} mais pas de chance pour moi, pouvez-vous m'aider .... ??
4
Existe-t-il une méthode created_at pour accéder à l'attribut @created_at? Quel genre d'objet est @created_at? Définit-il <=>? Quel genre d'erreurs obtenez-vous? etc, etc, ad nauseum. En d'autres termes, nous avons besoin de plus de détails que "mais pas de chance pour moi".
rampion
cela fonctionne si vous faites myarray = myarray.sort {...} sans "!"
DoruChidean
@Doru Oui, cela fonctionne aussi, mais pourquoi feriez-vous cela? C'est moins direct et moins efficace. À utiliser sortsi vous souhaitez conserver l'original et affecter le résultat à un objet différent; sinon, utilisez la variante en place, sort!. En fait, sortappelle en sort!interne, après avoir copié l'objet d'origine.
Konrad Rudolph
1
@Doru Il doit y avoir une erreur ailleurs dans le code, je peux vous garantir à 100% que sort!cela fonctionnera très bien, et toujours (!) Faire la même chose que ce que vous avez écrit.
Konrad Rudolph
27

Ordre croissant :

objects_array.sort! { |a, b|  a.attribute <=> b.attribute }

ou

objects_array.sort_by{ |obj| obj.attribute }

Ordre décroissant :

objects_array.sort! { |a, b|  b.attribute <=> a.attribute }

ou

objects_array.sort_by{ |obj| obj.attribute }.reverse
Shiva Kumar
la source
20

au cas où vous auriez besoin de trier par deux attributs, où le premier est plus important que le second (signifie ne prendre en compte les seconds arguments que si les premiers arguments sont égaux), alors vous pouvez faire comme ceci

myarray.sort{ |a,b| (a.attr1 == b.attr1) ? a.attr2 <=> b.attr2 : a.attr1 <=> b.attr1 }

ou en cas de tableau de tableaux

myarray.sort{ |a,b| (a[0] == b[0]) ? a[1] <=> b[1] : a[0] <=> b[0] }
bumbu
la source
14

Vous pouvez rendre n'importe quelle classe triable en remplaçant la méthode <=>:

class Person

  attr_accessor :first_name, :last_name

  def initialize(first_name, last_name)
    @first_name = first_name
    @last_name = last_name
  end

  def <=>(other)
    @last_name + @first_name <=> other.last_name + other.first_name
  end

end

Désormais, un tableau d'objets Person pourra être trié sur last_name.

ar = [Person.new("Eric", "Cunningham"), Person.new("Homer", "Allen")]

puts ar  # => [ "Eric Cunningham", "Homer Allen"]  (Person objects!)

ar.sort!

puts ar  # => [ "Homer Allen", "Eric Cunningham" ]
user1182000
la source
10

Le tri de tableau fonctionne bien, comme indiqué ci-dessus:

myarray.sort! { |a, b|  a.attribute <=> b.attribute }

MAIS, vous devez vous assurer que l' <=>opérateur est implémenté pour cet attribut. S'il s'agit d'un type de données natif Ruby, ce n'est pas un problème. Sinon, écrivez votre propre implémentation qui renvoie -1 si a <b, 0 si elles sont égales et 1 si a> b.

cjs
la source
10

Plus élégant objects.sort_by(&:attribute), vous pouvez en ajouter un .reversesi vous avez besoin de changer de commande.

Noah Davis
la source
-2
@model_name.sort! { |a,b| a.attribute <=> b.attribute }
Harisankar PS
la source
2
Il y a beaucoup de réponses identiques publiées en 2009. Inutile d'en ajouter une autre.
entre
2
Ne pas utiliser sortlorsque vous triez des objets qui ne peuvent pas être directement comparés. Si vous devez accéder à des attributs ou faire un calcul pour obtenir la valeur à comparer, utilisez sort_by. Ce sera beaucoup plus rapide.
The Tin Man