質問

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")

このトリックを使用すると、配列から複数の属性要素によって一意に選択できます。

@photos = @photos.uniq { |p| [p.album_id, p.author_id] }

私は当初、 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

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

enumerable_collection.uniq(&:property)

…読み心地も良くなります!

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

Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values

Rails には #uniq_by メソッドもあります - を参照してください。 パラメータ化された Array#uniq (つまり、uniq_by)

私は 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 属性の一意の場合ですが、辞書順ソートでも同じことができます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top