Rel pemfilteran array objek berdasarkan nilai atribut

96

Jadi saya melakukan kueri ke db dan saya memiliki array objek yang lengkap:

@attachments = Job.find(1).attachments

Sekarang saya memiliki array objek, saya tidak ingin melakukan kueri db lagi, tetapi saya ingin memfilter array berdasarkan Attachmentobjek file_typesehingga saya dapat memiliki daftar di attachmentsmana jenis file itu 'logo'dan kemudian daftar lain di attachmentsmana jenis filenya adalah'image'

Sesuatu seperti ini:

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

Namun dalam memori, bukan kueri db.

joepour
sumber
Sepertinya kasus penggunaan yang baik partition- contohnya di sini .
SRack

Jawaban:

172

Coba:

Ini bagus:

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

tetapi untuk performa, Anda tidak perlu mengulang @attachments dua kali:

@logos , @images = [], []
@attachments.each do |attachment|
  @logos << attachment if attachment.file_type == 'logo'
  @images << attachment if attachment.file_type == 'image'
end
Vik
sumber
2
Karena solusi @ Vik cukup ideal, saya hanya akan menambahkannya dalam kasus biner, Anda dapat menggunakan fungsi 'partisi' untuk membuatnya lebih manis. ruby-doc.org/core-1.9.3/Enumerable.html#method-i-partition
Vlad
Terima kasih @Vlad, itu keren, tetapi hanya mendukung jika kita perlu mengumpulkan hanya dua hal dari objek.
Vik
1
Ya, itulah mengapa saya mengatakan "biner" :). Pada soal tersebut ternyata ada pilihan logo atau gambar, jadi saya tambahkan ini untuk kelengkapan.
Vlad
8

Jika keterikatan Anda

@attachments = Job.find(1).attachments

Ini akan menjadi array objek lampiran

Gunakan metode pilih untuk memfilter berdasarkan tipe_file.

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

Ini tidak akan memicu kueri db apa pun.

Soundar Rathinasamy
sumber
2

sudahkah Anda mencoba eager loading?

@attachments = Job.includes(:attachments).find(1).attachments
Siwei Shen 申思维
sumber
Maaf, saya kurang jelas: bagaimana cara memfilter menurut nilai atribut objek tanpa mengulang melalui array?
joepour
Jika saya mengerti dengan benar, Anda menginginkan lebih sedikit kueri db, terutama, setelah kueri seperti @attachments = Job.first.attachmentsdieksekusi, Anda ingin mengulang @attachmentssementara itu Anda tidak ingin kueri db lagi. apakah ini yang ingin kamu lakukan?
Siwei Shen 申思维
Saya melakukan kueri db dan menerima berbagai objek. Saya kemudian ingin membuat dua daftar terpisah dari satu array itu dengan memfilter objek berdasarkan nilai atributnya (Lihat posting asli) - cheers
joepour
0

Anda dapat memfilter menggunakan mana

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

Saya akan melakukan ini sedikit berbeda. Susun kueri Anda untuk mengambil hanya yang Anda butuhkan dan pisahkan dari sana.

Jadi buat kueri Anda sebagai berikut:

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

Dan kemudian partisi data:

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

Itu akan mendapatkan data yang Anda cari dengan cara yang rapi dan efisien.

SRack
sumber