Ruby中的Uniq按对象属性
时间:2020-03-06 14:29:46 来源:igfitidea点击:
在数组中选择相对于一个或者多个属性唯一的对象的最优雅方法是什么?
这些对象存储在ActiveRecord中,因此使用AR的方法也可以。
解决方案
我最初建议在Array上使用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
。
不过,我不确定我们要在这里做什么。
如果我正确理解了问题,那么我将通过比较编组对象以确定是否任何属性发生变化的准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
现在,如果我们可以对属性值进行排序,则可以完成以下操作:
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属性的唯一标记,但是可以通过词典顺序完成相同的操作...
在数据库级别上执行此操作:
YourModel.find(:all, :group => "status")
我们可以使用哈希,每个哈希仅包含一个值:
Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values
在项目中将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
我喜欢jmah使用哈希来强制唯一性。这里有几种其他方法可以给猫咪蒙皮:
objs.inject({}) {|h,e| h[e.attr]=e; h}.values
那是一个很好的1-liner,但是我怀疑这可能会更快一些:
h = {} objs.each {|e| h[e.attr]=e} h.values