Jawaban:
Gunakan Array#uniq
dengan satu blok:
@photos = @photos.uniq { |p| p.album_id }
require 'backports'
:-)
@photos.uniq &:album_id
Tambahkan uniq_by
metode ke Array dalam proyek Anda. Ini bekerja dengan analogi dengan sort_by
. Begitu uniq_by
juga dengan uniq
apa sort_by
adanya sort
. Pemakaian:
uniq_array = my_array.uniq_by {|obj| obj.id}
Pelaksanaan:
class Array
def uniq_by(&blk)
transforms = []
self.select do |el|
should_keep = !transforms.include?(t=blk[el])
transforms << t
should_keep
end
end
end
Perhatikan bahwa ini mengembalikan larik baru daripada mengubah larik Anda saat ini. Kami belum menulis uniq_by!
metode tetapi harus cukup mudah jika Anda mau.
EDIT: Tribalvibes menunjukkan bahwa penerapannya adalah O (n ^ 2). Lebih baik menjadi sesuatu seperti (belum teruji) ...
class Array
def uniq_by(&blk)
transforms = {}
select do |el|
t = blk[el]
should_keep = !transforms[t]
transforms[t] = true
should_keep
end
end
end
Anda dapat menggunakan trik ini untuk memilih unik dengan beberapa elemen atribut dari array:
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
Saya awalnya menyarankan menggunakan select
metode pada Array. Yakni:
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}
memberi kami [2,4,6]
kembali.
Tetapi jika Anda menginginkan objek seperti itu pertama, gunakan detect
.
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}
memberi kami 4
.
Saya tidak yakin apa tujuan Anda di sini.
Jika saya memahami pertanyaan Anda dengan benar, saya telah mengatasi masalah ini menggunakan pendekatan semu-hacky untuk membandingkan objek Marshaled untuk menentukan apakah ada atribut yang bervariasi. Injeksi di akhir kode berikut akan menjadi contoh:
class Foo
attr_accessor :foo, :bar, :baz
def initialize(foo,bar,baz)
@foo = foo
@bar = bar
@baz = baz
end
end
objs = [Foo.new(1,2,3),Foo.new(1,2,3),Foo.new(2,3,4)]
# find objects that are uniq with respect to attributes
objs.inject([]) do |uniqs,obj|
if uniqs.all? { |e| Marshal.dump(e) != Marshal.dump(obj) }
uniqs << obj
end
uniqs
end
Cara paling elegan yang saya temukan adalah spin-off menggunakan Array#uniq
dengan satu blok
enumerable_collection.uniq(&:property)
… Terbaca lebih baik juga!
Gunakan Array # uniq dengan satu blok:
objects.uniq {|obj| obj.attribute}
Atau pendekatan yang lebih ringkas:
objects.uniq(&:attribute)
Rails juga memiliki a #uniq_by
metode.
Referensi: Parameterized Array # uniq (yaitu, uniq_by)
Saya suka jawaban jmah dan Head. Tapi apakah mereka mempertahankan urutan array? Mereka mungkin ada di versi ruby yang lebih baru karena ada beberapa persyaratan penyimpanan urutan penyisipan hash yang ditulis ke dalam spesifikasi bahasa, tetapi berikut adalah solusi serupa yang saya suka gunakan yang mempertahankan ketertiban.
h = Set.new
objs.select{|el| h.add?(el.attr)}
Implementasi ActiveSupport:
def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
Sekarang jika Anda dapat mengurutkan nilai atribut, ini dapat dilakukan:
class A
attr_accessor :val
def initialize(v); self.val = v; end
end
objs = [1,2,6,3,7,7,8,2,8].map{|i| A.new(i)}
objs.sort_by{|a| a.val}.inject([]) do |uniqs, a|
uniqs << a if uniqs.empty? || a.val != uniqs.last.val
uniqs
end
Itu untuk 1-atribut unik, tetapi hal yang sama dapat dilakukan dengan pengurutan leksikografis ...