1 つまたは複数の属性に関して一意な配列内のオブジェクトを選択する最もエレガントな方法は何ですか?
これらのオブジェクトは ActiveRecord に保存されるため、AR のメソッドを使用しても問題ありません。
1 つまたは複数の属性に関して一意な配列内のオブジェクトを選択する最もエレガントな方法は何ですか?
これらのオブジェクトは ActiveRecord に保存されるため、AR のメソッドを使用しても問題ありません。
Array#uniq
ブロックで使用:
@photos = @photos.uniq { |p| p.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
データベース レベルで実行します。
YourModel.find(:all, :group => "status")
私は当初select
、Array でメソッドを使用することを提案していました。ウィット:
[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
これは素晴らしいワンライナーですが、これは少し速いかもしれないと思います:
h = {}
objs.each {|e| h[e.attr]=e}
h.values
Array#uniq
私が見つけた最もエレガントな方法は、ブロックを使用したスピンオフです
enumerable_collection.uniq(&:property)
…それはあまりにもよく読みます!
あなたの質問を正しく理解していれば、マーシャリングされたオブジェクトを比較して属性が異なるかどうかを判断する準ハッキーなアプローチを使用して、この問題に取り組みました。次のコードの最後にある注入がその例です。
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
各キーに 1 つの値のみを含むハッシュを使用できます。
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
Railsにも#uniq_by
メソッドがあります。
ActiveSupport の実装:
def uniq_by
hash, array = {}, []
each { |i| hash[yield(i)] ||= (array << i) }
array
end
私はjmahとHeadの答えが好きです。しかし、それらは配列の順序を保持していますか? 言語仕様にハッシュの挿入順序を保持する要件がいくつか書かれているため、Ruby の新しいバージョンではそうなるかもしれませんが、私が好んで使用する同様の解決策は、関係なく順序を保持します。
h = Set.new
objs.select{|el| h.add?(el.attr)}
これで、属性値で並べ替えることができれば、これを行うことができます。
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属性の一意の場合ですが、辞書式順序を使用して同じことを行うことができます...