137

1 つまたは複数の属性に関して一意な配列内のオブジェクトを選択する最もエレガントな方法は何ですか?

これらのオブジェクトは ActiveRecord に保存されるため、AR のメソッドを使用しても問題ありません。

4

15 に答える 15

219

Array#uniqブロックで使用:

@photos = @photos.uniq { |p| p.album_id }
于 2012-04-10T05:57:30.980 に答える
22

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
于 2008-09-22T08:18:32.103 に答える
17

データベース レベルで実行します。

YourModel.find(:all, :group => "status")
于 2008-09-21T01:42:06.527 に答える
6

私は当初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ます。

でも、あなたがここで何をしようとしているのかはわかりません。

于 2008-09-20T23:45:15.123 に答える
5

私は jmah が一意性を強制するためにハッシュを使用するのが好きです。その猫の皮を剥ぐには、さらにいくつかの方法があります。

objs.inject({}) {|h,e| h[e.attr]=e; h}.values

これは素晴らしいワンライナーですが、これは少し速いかもしれないと思います:

h = {}
objs.each {|e| h[e.attr]=e}
h.values
于 2008-10-23T21:11:26.460 に答える
4

Array#uniq私が見つけた最もエレガントな方法は、ブロックを使用したスピンオフです

enumerable_collection.uniq(&:property)

…それはあまりにもよく読みます!

于 2017-09-20T22:55:33.097 に答える
3

あなたの質問を正しく理解していれば、マーシャリングされたオブジェクトを比較して属性が異なるかどうかを判断する準ハッキーなアプローチを使用して、この問題に取り組みました。次のコードの最後にある注入がその例です。

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
于 2008-09-20T23:59:26.243 に答える
2

各キーに 1 つの値のみを含むハッシュを使用できます。

Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
于 2008-09-21T01:48:53.047 に答える
2

Railsにも#uniq_byメソッドがあります。

参照:パラメータ化された Array#uniq (つまり、uniq_by)

于 2011-05-10T05:03:10.060 に答える
1

ActiveSupport の実装:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end
于 2012-07-18T22:31:34.960 に答える
1

私はjmahとHeadの答えが好きです。しかし、それらは配列の順序を保持していますか? 言語仕様にハッシュの挿入順序を保持する要件がいくつか書かれているため、Ruby の新しいバージョンではそうなるかもしれませんが、私が好んで使用する同様の解決策は、関係なく順序を保持します。

h = Set.new
objs.select{|el| h.add?(el.attr)}
于 2012-03-11T16:15:53.887 に答える
0

これで、属性値で並べ替えることができれば、これを行うことができます。

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属性の一意の場合ですが、辞書式順序を使用して同じことを行うことができます...

于 2008-09-21T01:11:18.113 に答える