Rails filtrando matriz de objetos por valor de atributo

95

Então, eu executo uma consulta ao banco de dados e tenho uma matriz completa de objetos:

@attachments = Job.find(1).attachments

Agora que tenho uma matriz de objetos, não quero realizar outra consulta de banco de dados, mas gostaria de filtrar a matriz com base nos Attachmentobjetos file_typepara que eu possa ter uma lista de attachmentsonde está o tipo de arquivo 'logo'e, em seguida, outra lista de attachmentsonde o tipo de arquivo é'image'

Algo assim:

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

Mas na memória, em vez de uma consulta db.

joepour
fonte
Parece um bom caso de uso partition- por exemplo, aqui .
SRack

Respostas:

172

Experimentar :

Isto é bom :

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

mas em termos de desempenho, você não precisa iterar @attachments duas vezes:

@logos , @images = [], []
@attachments.each do |attachment|
  @logos << attachment if attachment.file_type == 'logo'
  @images << attachment if attachment.file_type == 'image'
end
Vik
fonte
2
Como a solução de @Vik é bastante ideal, acrescentarei que, em casos binários, você poderia usar uma função de 'partição' para tornar as coisas mais fáceis. ruby-doc.org/core-1.9.3/Enumerable.html#method-i-partition
Vlad
Obrigado @Vlad, isso é legal, mas dá suporte apenas se precisarmos coletar apenas duas coisas do objeto.
Vik de
1
Sim, é por isso que disse "binário" :). Na pergunta, aparentemente havia uma escolha de logotipo ou imagem, então eu adicionei isso para completar.
Vlad
8

Se seus anexos são

@attachments = Job.find(1).attachments

Esta será uma matriz de objetos de anexo

Use o método select para filtrar com base em file_type.

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

Isso não acionará nenhuma consulta de banco de dados.

Soundar Rathinasamy
fonte
2

você já tentou carregar antecipadamente?

@attachments = Job.includes(:attachments).find(1).attachments
Siwei Shen 申思维
fonte
Desculpe, não estou sendo claro: como faço para filtrar pelo valor de um atributo de objeto sem percorrer a matriz?
joepour
Se bem entendi, você quer menos consulta de banco de dados, especialmente, uma vez que uma consulta como @attachments = Job.first.attachmentsexecutada, você deseja fazer um loop, @attachmentsenquanto isso, você não quer mais consultas de banco de dados. é isso que você quer fazer?
Siwei Shen 申思维
Eu faço uma consulta de banco de dados e recebo uma matriz de objetos. Em seguida, quero criar duas listas separadas desse array filtrando os objetos com base no valor de seus atributos (veja a postagem original)
saúde
0

Você pode filtrar usando onde

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

Eu faria isso de maneira um pouco diferente. Estruture sua consulta para recuperar apenas o que você precisa e divida a partir daí.

Portanto, faça sua consulta da seguinte maneira:

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

E então particionar os dados:

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

Isso obterá os dados que você procura de maneira organizada e eficiente.

SRack
fonte