В качестве альтернативы вы также можете использовать filter
с выражением лямбда для получения желаемого результата. Например:
>>> l1 = [1,2,6,8]
>>> l2 = set([2,3,5,8])
# v `filter` returns the a iterator object. Here I'm type-casting
# v it to `list` in order to display the resultant value
>>> list(filter(lambda x: x not in l2, l1))
[1, 6]
Сравнение производительности
Здесь я сравниваю производительность всех ответов, упомянутых здесь. Как и ожидалось, операция Arkku set
выполняется быстрее.
mquadri$ python -m timeit -s "l1 = set([1,2,6,8]); l2 = set([2,3,5,8]);" "l1 - l2"
10000000 loops, best of 3: 0.124 usec per loop
set
- Второй (0,302 мксек за цикл) mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.302 usec per loop
mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "[x for x in l1 if x not in l2]"
1000000 loops, best of 3: 0.552 usec per loop
filter
- Четвертый (0,972 мксек за цикл) mquadri$ python -m timeit -s "l1 = [1,2,6,8]; l2 = set([2,3,5,8]);" "filter(lambda x: x not in l2, l1)"
1000000 loops, best of 3: 0.972 usec per loop
reduce
+ filter
- Fifth (3.97 usec за цикл) mquadri$ python -m timeit "l1 = [1,2,6,8]; l2 = [2,3,5,8];" "reduce(lambda x,y : filter(lambda z: z!=y,x) ,l1,l2)"
100000 loops, best of 3: 3.97 usec per loop
PS: set
не поддерживают порядок и удаляют дубликат элементов из списка. Следовательно, не используйте разницу в настройках , если вам нужно какое-либо из них.
Теперь, если можно отсортировать на значениях атрибута, это может быть сделано:
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 атрибута, но то же самое может быть сделано w/лексикографический вид...
Мне нравится, когда использование jmah Хеша осуществляет уникальность. Вот пара большего количества способов освежевать ту кошку:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
Это - хороший 1 лайнер, но я подозреваю, что это могло бы быть немного быстрее:
h = {}
objs.each {|e| h[e.attr]=e}
h.values
Я первоначально предложил использовать 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
.
я не уверен, для чего Вы идете сюда, все же.
Сделайте это на уровне базы данных:
YourModel.find(:all, :group => "status")
Если я понимаю Ваш вопрос правильно, я занялся этой проблемой с помощью quasi-hacky подхода сравнения Упорядоченных объектов определить, варьируются ли какие-либо атрибуты. Введение в конце следующего кода было бы примером:
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
Добавьте 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!
метод, но должно быть достаточно легко, если бы Вы хотели.
РЕДАКТИРОВАНИЕ: 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
Можно использовать хеш, который содержит только одно значение для каждого ключа:
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values