Uniq by Ruby中的object属性

时间:2008-09-20 23:39:18

标签: ruby-on-rails ruby arrays unique

选择数组中对于一个或多个属性唯一的对象的最优雅方法是什么?

这些对象存储在ActiveRecord中,因此使用AR的方法也可以。

14 个答案:

答案 0 :(得分:181)

Array#uniq与块一起使用:

@photos = @photos.uniq { |p| p.album_id }

答案 1 :(得分:21)

uniq_by方法添加到项目中的Array。它与sort_by类似。因此uniq_byuniqsort_bysort。用法:

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

答案 2 :(得分:16)

在数据库级别上执行:

YourModel.find(:all, :group => "status")

答案 3 :(得分:7)

您可以使用此技巧通过数组中的多个属性元素选择唯一:

@photos = @photos.uniq { |p| [p.album_id, p.author_id] }

答案 4 :(得分:6)

我最初建议在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

但我不确定你在这里会发生什么。

答案 5 :(得分:5)

我喜欢jmah使用Hash来强制执行唯一性。以下是另外几种皮肤猫的方法:

objs.inject({}) {|h,e| h[e.attr]=e; h}.values

这是一个很好的1-liner,但我怀疑这可能会更快一点:

h = {}
objs.each {|e| h[e.attr]=e}
h.values

答案 6 :(得分:3)

如果我正确理解你的问题,我已经使用比较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

答案 7 :(得分:3)

我找到的最优雅的方法是使用带有块的Array#uniq分拆

enumerable_collection.uniq(&:property)

......它也读得更好!

答案 8 :(得分:2)

您可以使用哈希,每个密钥只包含一个值:

Hash[*recs.map{|ar| [ar[attr],ar]}.flatten].values

答案 9 :(得分:2)

Rails还有#uniq_by方法 - 请参阅Parameterized Array#uniq (i.e., uniq_by)

答案 10 :(得分:1)

我喜欢jmah和Head的答案。但它们是否保留了数组顺序?它们可能在更新版本的ruby中,因为已经有一些写入语言规范的哈希插入顺序保留要求,但这里有一个类似的解决方案,我喜欢使用它保留顺序,无论如何。

h = Set.new
objs.select{|el| h.add?(el.attr)}

答案 11 :(得分:1)

ActiveSupport实施:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
end

答案 12 :(得分:0)

现在,如果您可以对属性值进行排序,则可以这样做:

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属性的唯一,但同样的事情可以用词典排序...

答案 13 :(得分:0)

Array#uniq与块配合使用:

objects.uniq {|obj| obj.attribute}

或更简洁的方法:

objects.uniq(&:attribute)