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.


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

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.


2

avez-vous essayé le chargement impatient?

@attachments = Job.includes(:attachments).find(1).attachments

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"])

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.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.