Відповіді:
Використовувати Array#uniq
з блоком:
@photos = @photos.uniq { |p| p.album_id }
require 'backports'
:-)
@photos.uniq &:album_id
Додайте uniq_by
метод до масиву у свій проект. Це працює за аналогією з 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!
метод, але він повинен бути досить легким, якщо ви цього хочете.
EDIT: 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
метод на 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
.
Я не впевнений, що ти тут збираєшся.
Якщо я правильно розумію ваше запитання, я вирішив цю проблему, використовуючи квазі-хакі-підхід порівняння об'єктів Маршальованих, щоб визначити, чи змінюються якісь атрибути. Ін'єктом в кінці наступного коду буде приклад:
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)
… Воно також читається краще!
Використовуйте масив # uniq з блоком:
objects.uniq {|obj| obj.attribute}
Або більш стислий підхід:
objects.uniq(&:attribute)
Рейки також мають #uniq_by
метод.
Мені подобаються відповіді 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-атрибута унікальний, але те ж саме можна зробити з лексикографічним сортуванням ...