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

这些对象存储在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] }

我最初建议在Array上使用 select 方法。即:

[1,2,3,4,5,6,7]。选择{| e | e%2 == 0} 给我们 [2,4,6]

但是如果你想要第一个这样的对象,请使用 detect

[1,2,3,4,5,6,7] .detect {| e | e&gt; 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

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

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

enumerable_collection.uniq(&:property)

&#8230;它也读得更好!

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

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

Rails还有#uniq_by方法 - 请参阅参数化数组#uniq(即uniq_by)

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

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

ActiveSupport实施:

def uniq_by
  hash, array = {}, []
  each { |i| hash[yield(i)] ||= (array << i) }
  array
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属性的唯一,但同样的事情可以用词典排序...

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top