Frage

Was ist die eleganteste Art und Weise Objekte auswählen aus in einem Array, das in Bezug auf ein oder mehrere Attribute eindeutig sind?

Diese Objekte werden in Active gespeichert, so AR Methoden verwenden wäre auch in Ordnung.

War es hilfreich?

Lösung

Verwenden Sie Array#uniq mit einem Block:

@photos = @photos.uniq { |p| p.album_id }

Andere Tipps

Fügen Sie die uniq_by Methode Array in Ihrem Projekt. Es funktioniert analog zu sort_by. So uniq_by ist zu uniq als sort_by sort ist. Verbrauch:

uniq_array = my_array.uniq_by {|obj| obj.id}

Die Umsetzung:

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

Beachten Sie, dass es ein neues Array zurückgibt, anstatt Ihre aktuelle anstelle ändern. Wir haben keine uniq_by! Methode geschrieben, aber es sollte einfach genug sein, wenn man will.

EDIT: Tribalvibes weist darauf hin, dass diese Implementierung ist O (n ^ 2). Besser wäre so etwas wie (nicht getestet) ...

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

Machen Sie es auf der Datenbank-Ebene:

YourModel.find(:all, :group => "status")

Sie können diesen Trick verwenden, um mehrere Attribute Elemente von Array einzigartig wählen:

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

Ich hatte ursprünglich vorgeschlagen, die select Methode auf Array verwendet wird. Nämlich:

[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0} gibt uns [2,4,6] zurück.

Aber wenn Sie das erste solches Objekt möchten, verwenden Sie detect.

[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3} gibt uns 4.

Ich bin nicht sicher, was Sie hier gehen, though.

Ich mag JMAH die Verwendung einer Hash Eindeutigkeit zu erzwingen. Hier ein paar mehr Möglichkeiten, um die Haut, die Katze:

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

Das ist ein schöner 1-liner, aber ich vermute, dies könnte ein wenig schneller sein:

h = {}
objs.each {|e| h[e.attr]=e}
h.values

Wenn ich Ihre Frage richtig verstanden hat, habe ich dieses Problem mit dem quasi-Hacky Ansatz in Angriff genommen, die serialisiert Objekte zu vergleichen, um zu bestimmen, ob irgendwelche Attribute variieren. Die inject am Ende des folgenden Codes wären ein Beispiel:

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

Die eleganteste Art, wie ich gefunden habe, ist ein Spin-off mit Array#uniq mit einem Block

enumerable_collection.uniq(&:property)

... es liest auch besser!

Sie können einen Hash verwenden, die nur einen Wert für jeden Schlüssel enthält:

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

Rails hat auch eine #uniq_by Methode - Parameterized Array # uniq sehen (dh uniq_by)

Ich mag JMAH und Head Antworten. Aber erhalten sie Array Ordnung? Sie könnten in späteren Versionen von Rubin, da es einige Hash-Insertion Ordnung erhalt Anforderungen in die Sprache Spezifikation geschrieben, aber hier ist eine ähnliche Lösung, die Ich mag, dass, um unabhängig bewahrt verwenden.

h = Set.new
objs.select{|el| h.add?(el.attr)}

Active Umsetzung:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end

Nun, wenn Sie auf die Attributwerte sortieren kann dies getan werden kann:

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

Das ist für ein 1-Attribut einzigartig, aber das gleiche kann man w / lexikographischer Art geschehen ...

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top