选择数组中对于一个或多个属性唯一的对象的最优雅方法是什么?
这些对象存储在ActiveRecord中,因此使用AR的方法也可以.
使用Array#uniq
带块:
@photos = @photos.uniq { |p| p.album_id }
将uniq_by
方法添加到项目中的Array.它的作用类似于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
在数据库级别上执行:
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
.
不过,我不确定你的目的是什么.
我喜欢jmah使用Hash来强制执行唯一性.这里有几种皮肤猫的方法:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
这是一个不错的1-liner,但我怀疑这可能会更快一点:
h = {} objs.each {|e| h[e.attr]=e} h.values