Rails filtrant un tableau d'objets par valeur d'attribut

96

J'effectue donc une requête à la base de données et j'ai un tableau complet d'objets:

@attachments = Job.find(1).attachments

Maintenant que j'ai un tableau d'objets, je ne veux pas effectuer une autre requête de base de données, mais je voudrais filtrer le tableau en fonction de l' Attachmentobjet file_typeafin que je puisse avoir une liste de l' attachmentsemplacement du type de fichier 'logo', puis une autre liste de l' attachmentsemplacement le type de fichier est'image'

Quelque chose comme ça:

@logos  = @attachments.where("file_type = ?", 'logo')
@images = @attachments.where("file_type = ?", 'image')

Mais en mémoire au lieu d'une requête db.

Joepour
la source
On dirait un bon cas d'utilisation pour partition- par exemple ici .
SRack

Réponses:

172

Essayez:

C'est bon :

@logos = @attachments.select { |attachment| attachment.file_type == 'logo' }
@images = @attachments.select { |attachment| attachment.file_type == 'image' }

mais pour les performances, vous n'avez pas besoin d'itérer deux fois @attachments:

@logos , @images = [], []
@attachments.each do |attachment|
  @logos << attachment if attachment.file_type == 'logo'
  @images << attachment if attachment.file_type == 'image'
end
Vik
la source
2
Comme la solution de @ Vik est à peu près idéale, j'ajouterai simplement que dans les cas binaires, vous pouvez utiliser une fonction de «partition» pour rendre les choses douces. ruby-doc.org/core-1.9.3/Enumerable.html#method-i-partition
Vlad
Merci @Vlad, c'est cool, mais cela ne prend en charge que si nous devons collecter seulement deux choses de l'objet.
Vik
1
Oui, c'est pourquoi j'ai dit "binaire" :). Dans la question, il y avait apparemment un choix de logo ou d'image, alors j'ai ajouté ceci par souci d'exhaustivité.
Vlad
8

Si vos pièces jointes sont

@attachments = Job.find(1).attachments

Ce sera un tableau d'objets de pièce jointe

Utilisez la méthode de sélection pour filtrer en fonction de file_type.

@logos = @attachments.select { |attachment| attachment.file_type == 'logo' }
@images = @attachments.select { |attachment| attachment.file_type == 'image' }

Cela ne déclenchera aucune requête de base de données.

Soundar Rathinasamy
la source
2

avez-vous essayé le chargement impatient?

@attachments = Job.includes(:attachments).find(1).attachments
Siwei Shen 申思维
la source
Désolé, je ne suis pas clair: comment filtrer par la valeur d'un attribut d'objet sans boucler le tableau?
joepour
Si j'ai bien compris, vous voulez moins de requêtes de base de données, en particulier, une fois qu'une requête telle que @attachments = Job.first.attachmentsexécutée, vous voulez faire une boucle @attachmentspendant que vous ne voulez plus de requêtes de base de données. c'est ça que tu veux faire?
Siwei Shen 申思维
Je fais une requête de base de données et reçois un tableau d'objets. Je veux ensuite créer deux listes distinctes à partir de ce tableau en filtrant les objets en fonction de la valeur de leurs attributs (voir l'article original) - cheers
joepour
0

Vous pouvez filtrer en utilisant où

Job.includes(:attachments).where(file_type: ["logo", "image"])
Darlan Dieterich
la source
0

Je procéderais légèrement différemment. Structurez votre requête de manière à ne récupérer que ce dont vous avez besoin et à partir de là.

Alors faites votre requête comme suit:

#                                vv or Job.find(1) vv
attachments = Attachment.where(job_id: @job.id, file_type: ["logo", "image"])
# or 
Job.includes(:attachments).where(id: your_job_id, attachments: { file_type: ["logo", "image"] })

Et puis partitionnez les données:

@logos, @images = attachments.partition { |attachment| attachment.file_type == "logo" }

Cela obtiendra les données que vous recherchez d'une manière ordonnée et efficace.

SRack
la source