Rieles que filtran la matriz de objetos por valor de atributo

96

Entonces realizo una consulta a la base de datos y tengo una matriz completa de objetos:

@attachments = Job.find(1).attachments

Ahora que tengo una matriz de objetos, no quiero realizar otra consulta de base de datos, pero me gustaría filtrar la matriz en función del Attachmentobjeto file_typepara poder tener una lista de attachmentsdónde está el tipo de archivo 'logo'y luego otra lista de attachmentsdónde el tipo de archivo es'image'

Algo como esto:

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

Pero en la memoria en lugar de una consulta de base de datos.

joepour
fuente
Parece una buena caso de uso para partition- ejemplo aquí .
SRack el

Respuestas:

172

Tratar :

Esto esta bien :

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

pero para el rendimiento, no es necesario repetir @attachments dos veces:

@logos , @images = [], []
@attachments.each do |attachment|
  @logos << attachment if attachment.file_type == 'logo'
  @images << attachment if attachment.file_type == 'image'
end
Vik
fuente
2
Como la solución de @ Vik es bastante ideal, solo agregaré que en casos binarios, podría usar una función de 'partición' para mejorar las cosas. ruby-doc.org/core-1.9.3/Enumerable.html#method-i-partition
Vlad
Gracias @Vlad, eso es genial, pero solo es compatible si necesitamos recopilar solo dos cosas del objeto.
Vik
1
Sí, por eso dije "binario" :). En la pregunta, aparentemente había una opción de logotipo o imagen, así que agregué esto para completar.
Vlad
8

Si sus archivos adjuntos son

@attachments = Job.find(1).attachments

Esta será una matriz de objetos adjuntos

Utilice el método de selección para filtrar según el tipo de archivo.

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

Esto no activará ninguna consulta de base de datos.

Soundar Rathinasamy
fuente
2

has probado la carga ansiosa?

@attachments = Job.includes(:attachments).find(1).attachments
Siwei Shen 申思维
fuente
Lo siento, no estoy siendo claro: ¿cómo filtro por el valor de un atributo de objeto sin recorrer la matriz?
joepour
Si entendí correctamente, desea menos consultas de base de datos, especialmente, una vez que se @attachments = Job.first.attachmentsejecuta una consulta , desea realizar un bucle @attachmentsmientras tanto, no desea más consultas de base de datos. ¿Es esto lo que quieres hacer?
Siwei Shen 申思维
Hago una consulta de base de datos y recibo una matriz de objetos. Luego quiero crear dos listas separadas de esa matriz filtrando los objetos según el valor de sus atributos (Ver publicación original) - saludos
joepour
0

Puedes filtrar usando donde

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

Haría esto de manera ligeramente diferente. Estructura tu consulta para recuperar solo lo que necesitas y partir de ahí.

Entonces haga su consulta lo siguiente:

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

Y luego particione los datos:

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

Eso obtendrá los datos que busca de una manera ordenada y eficiente.

SRack
fuente