1つ以上の属性に関して一意である配列内のオブジェクトを選択する最もエレガントな方法は何ですか?
これらのオブジェクトはActiveRecordに保存されるため、ARのメソッドを使用しても問題ありません。
つかいます - Array#uniq
ブロック付き:
@photos = @photos.uniq { |p| p.album_id }
uniq_by
メソッドをプロジェクトの配列に追加します。 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
データベースレベルで実行します。
YourModel.find(:all, :group => "status")
このトリックを使用して、配列からいくつかの属性要素によって一意を選択できます。
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
元々、Arrayでselect
メソッドを使用することを提案していました。機知に:
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}
は[2,4,6]
戻る。
ただし、最初のそのようなオブジェクトが必要な場合は、detect
を使用します。
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}
は4
。
しかし、私はあなたが何のためにここに行くのかわかりません。
Jmahがハッシュを使用して一意性を強制するのが好きです。その猫の皮を剥ぐいくつかの方法があります:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
これはすてきな1行ですが、これは少し速いかもしれません。
h = {}
objs.each {|e| h[e.attr]=e}
h.values
あなたの質問を正しく理解したら、マーシャリングされたオブジェクトを比較して属性が変化するかどうかを判断する準ハック手法を使用して、この問題に取り組みました。次のコードの最後にある注入は一例です。
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メソッドもあります- Parameterized Array#uniq(i.e.、uniq_by) を参照してください
ハッシュを使用できます。ハッシュには、キーごとに1つの値のみが含まれます。
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
私はjmahとHeadの答えが好きです。しかし、配列の順序は保持されますか? Rubyの以降のバージョンでは、ハッシュの挿入順序を維持する要件が言語仕様に記述されているため、それらは可能性があります。
h = Set.new
objs.select{|el| h.add?(el.attr)}
ActiveSupportの実装:
def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
これで、属性値でソートできる場合、これを実行できます。
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属性固有ですが、辞書編集ソートを使用して同じことができます...