Ruby 中按对象属性的 Uniq
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/109781/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Uniq by object attribute in Ruby
提问by sutee
What's the most elegant way to select out objects in an array that are unique with respect to one or more attributes?
在数组中选择一个或多个属性唯一的对象的最优雅方法是什么?
These objects are stored in ActiveRecord so using AR's methods would be fine too.
这些对象存储在 ActiveRecord 中,因此使用 AR 的方法也可以。
回答by Lane
回答by Daniel Lucraft
Add the uniq_bymethod to Array in your project. It works by analogy with sort_by. So uniq_byis to uniqas sort_byis to sort. Usage:
将该uniq_by方法添加到项目中的 Array 中。它的工作原理与sort_by. 所以,uniq_by是uniq的sort_by是sort。用法:
uniq_array = my_array.uniq_by {|obj| obj.id}
The implementation:
实施:
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
Note that it returns a new array rather than modifying your current one in place. We haven't written a uniq_by!method but it should be easy enough if you wanted to.
请注意,它返回一个新数组,而不是就地修改当前数组。我们还没有编写uniq_by!方法,但如果您愿意,它应该很容易。
EDIT: Tribalvibes points out that that implementation is O(n^2). Better would be something like (untested)...
编辑: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
回答by mislav
Do it on the database level:
在数据库级别执行此操作:
YourModel.find(:all, :group => "status")
回答by YauheniNinja
You can use this trick to select unique by several attributes elements from array:
您可以使用此技巧从数组中选择唯一的几个属性元素:
@photos = @photos.uniq { |p| [p.album_id, p.author_id] }
回答by Alex M
I had originally suggested using the selectmethod on Array. To wit:
我最初建议select在 Array 上使用该方法。以机智:
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}gives us [2,4,6]back.
[1, 2, 3, 4, 5, 6, 7].select{|e| e%2 == 0}还给我们[2,4,6]。
But if you want the first such object, use detect.
但是,如果您想要第一个这样的对象,请使用detect.
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}gives us 4.
[1, 2, 3, 4, 5, 6, 7].detect{|e| e>3}给我们4。
I'm not sure what you're going for here, though.
不过,我不确定你要来这里做什么。
回答by Head
I like jmah's use of a Hash to enforce uniqueness. Here's a couple more ways to skin that cat:
我喜欢 jmah 使用哈希来强制唯一性。这是给那只猫剥皮的更多方法:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
That's a nice 1-liner, but I suspect this might be a little faster:
这是一个不错的 1-liner,但我怀疑这可能会快一点:
h = {}
objs.each {|e| h[e.attr]=e}
h.values
回答by Drew Olson
If I understand your question correctly, I've tackled this problem using the quasi-hacky approach of comparing the Marshaled objects to determine if any attributes vary. The inject at the end of the following code would be an example:
如果我正确理解了您的问题,我已经使用比较 Marshaled 对象的准 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
回答by iGbanam
The most elegant way I have found is a spin-off using Array#uniqwith a block
我发现的最优雅的方法是使用Array#uniq块进行分拆
enumerable_collection.uniq(&:property)
…it reads better too!
……它读起来也更好!
回答by Muhamad Najjar
Use Array#uniqwith a block:
将Array#uniq与块一起使用:
objects.uniq {|obj| obj.attribute}
Or a more concise approach:
或者更简洁的方法:
objects.uniq(&:attribute)
回答by jmah
You can use a hash, which contains only one value for each key:
您可以使用散列,其中每个键仅包含一个值:
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values

