106 votes

ActiveRecord.trouver(array_of_ids), le maintien de l'ordre

Lorsque vous effectuez Something.find(array_of_ids) dans les Rails, l'ordre du tableau qui en résulte ne dépend pas de l'ordre du array_of_ids.

Est-il possible de faire de la trouver et de conserver l'ordre?

ATM-je trier manuellement les enregistrements en fonction de l'ordre d'Id, mais c'est le genre de boiteux.

UPD: si il est possible de spécifier l'ordre à l'aide de l' :order param et une sorte de clause SQL, alors comment?

80voto

Gunchars Points 699

Bizarrement, personne n'a suggéré quelque chose comme ceci:

index = Something.find(array_of_ids).group_by(&:id)
array_of_ids.map { |i| index[i].first }

Aussi efficace qu'elle obtient d'ailleurs de laisser SQL backend de le faire.

Edit: Pour améliorer ma propre réponse, vous pouvez aussi faire comme ceci:

Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values

#index_by et #slice sont assez pratique ajouts dans ActiveSupport pour les tableaux et les tables de hachage, respectivement.

72voto

kovyrin Points 484

La réponse est seulement pour mysql

Il y a une fonction dans mysql appelée CHAMP()

Voici comment vous pourriez l'utiliser dans .find():

>> ids = [100, 1, 6]
=> [100, 1, 6]

>> WordDocument.find(ids).collect(&:id)
=> [1, 6, 100]

>> WordDocument.find(ids, :order => "field(id, #{ids.join(',')})").collect(&:id)
=> [100, 1, 6]

5voto

Omar Qureshi Points 5755

Pas possible en SQL qui fonctionne dans tous les cas, malheureusement, vous devez écrire seul trouve pour chaque enregistrement ou de l'ordonnance en ruby, bien qu'il n'y a probablement un moyen de le faire fonctionner à l'aide de techniques propriétaires:

Premier exemple:

sorted = arr.inject([]){|res, val| res << Model.find(val)}

TRÈS INEFFICACE

Deuxième exemple:

unsorted = Model.find(arr)
sorted = arr.inject([]){|res, val| res << unsorted.detect {|u| u.id == val}}

2voto

Chrisbloom7 Points 1386

@Gunchars réponse est grande, mais il ne fonctionne pas de la case dans les Rails 2.3 parce que la classe Hash n'est pas ordonné. Une solution simple est d'étendre la classe Enumerable' index_by à utiliser le OrderedHash classe:

module Enumerable
  def index_by_with_ordered_hash
    inject(ActiveSupport::OrderedHash.new) do |accum, elem|
      accum[yield(elem)] = elem
      accum
    end
  end
  alias_method_chain :index_by, :ordered_hash
end

Maintenant @Gunchars' approche de travail

Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values

Bonus

module ActiveRecord
  class Base
    def self.find_with_relevance(array_of_ids)
      array_of_ids = Array(array_of_ids) unless array_of_ids.is_a?(Array)
      self.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
    end
  end
end

Alors

Something.find_with_relevance(array_of_ids)

2voto

Mike Woodhouse Points 27748

Sous le capot, find avec un tableau d'identifiants de générer un SELECT avec un WHERE id IN... clause, qui devrait être plus efficace qu'en parcourant les id.

Si la demande est satisfaite dans un voyage à la base de données, mais SELECTs sans ORDER BY clauses ne sont pas triés. ActiveRecord le comprend, donc nous étendre notre find comme suit:

Something.find(array_of_ids, :order => 'id')

Si l'ordre des id dans votre tableau est arbitraire et significatif (c'est à dire que vous voulez que l'ordre des lignes retournées pour correspondre à votre tableau, indépendamment de la séquence de l'id de contenu) alors je pense que vous seriez mieux de serveur par le post-traitement des résultats dans le code, vous pouvez construire un :order de la clause, mais il serait extrêmement compliqué et pas du tout l'intention révélateur.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X