回答:
Array#uniq
ブロックで使用:
@photos = @photos.uniq { |p| p.album_id }
require 'backports'
:-)
@photos.uniq &:album_id
uniq_by
プロジェクトのArrayにメソッドを追加します。それはとの類推によって機能しsort_by
ます。そうuniq_by
しているuniq
ようsort_by
にですsort
。使用法:
uniq_array = my_array.uniq_by {|obj| obj.id}
実装:
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
現在の配列を変更するのではなく、新しい配列を返すことに注意してください。私たちは書いていないuniq_by!
メソッドが、必要に応じて簡単に作成できます。
編集:Tribalvibesは、その実装はO(n ^ 2)であると指摘しています。(テストされていない)のようなものが良いでしょう...
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
このトリックを使用して、配列からいくつかの属性要素によって一意を選択できます。
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
私があなたの質問を正しく理解していれば、マーシャリングされたオブジェクトを比較して属性が変化するかどうかを判断するという準ハックなアプローチを使用して、この問題に取り組みました。次のコードの最後にあるインジェクトがその例です。
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
Railsにも #uniq_by
メソッドがあります。
ここで、属性値でソートできる場合、これを行うことができます。
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
これは1属性の一意のものですが、同じことを辞書式ソートで実行できます...