Risposte:
Utilizzare Array#uniq
con un blocco:
@photos = @photos.uniq { |p| p.album_id }
require 'backports'
:-)
@photos.uniq &:album_id
Aggiungi il uniq_by
metodo ad Array nel tuo progetto. Funziona per analogia con sort_by
. Così uniq_by
è uniq
come sort_by
è sort
. Uso:
uniq_array = my_array.uniq_by {|obj| obj.id}
L'implemento:
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
Nota che restituisce un nuovo array invece di modificare quello attuale in posizione. Non abbiamo scritto un uniq_by!
metodo ma dovrebbe essere abbastanza facile se lo desideri.
EDIT: Tribalvibes sottolinea che tale implementazione è O (n ^ 2). Sarebbe meglio qualcosa come (non testato) ...
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
Fallo a livello di database:
YourModel.find(:all, :group => "status")
Puoi usare questo trucco per selezionare univoco da diversi elementi di attributi dall'array:
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
Inizialmente avevo suggerito di utilizzare il select
metodo su Array. Vale a dire:
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}
ci [2,4,6]
restituisce.
Ma se vuoi il primo oggetto del genere, usa detect
.
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}
ci dà 4
.
Non sono sicuro di cosa stai cercando qui, però.
Se capisco correttamente la tua domanda, ho affrontato questo problema utilizzando l'approccio quasi hacker di confrontare gli oggetti Marshaled per determinare se qualche attributo varia. L'inject alla fine del codice seguente sarebbe un esempio:
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
Il modo più elegante che ho trovato è uno spin-off che utilizza Array#uniq
con un blocco
enumerable_collection.uniq(&:property)
... si legge anche meglio!
Usa Array # uniq con un blocco:
objects.uniq {|obj| obj.attribute}
O un approccio più conciso:
objects.uniq(&:attribute)
Rails ha anche un #uniq_by
metodo.
Riferimento: array parametrizzato # uniq (ovvero uniq_by)
Mi piacciono le risposte di Jmah e Head. Ma preservano l'ordine degli array? Potrebbero nelle versioni successive di ruby poiché ci sono stati alcuni requisiti di conservazione dell'ordine di inserimento hash scritti nelle specifiche del linguaggio, ma ecco una soluzione simile che mi piace usare che preserva l'ordine a prescindere.
h = Set.new
objs.select{|el| h.add?(el.attr)}
Ora, se puoi ordinare i valori degli attributi, puoi farlo:
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
Questo è per un attributo unico, ma la stessa cosa può essere fatta con l'ordinamento lessicografico ...