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

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 20:40:42  来源:igfitidea点击:

Uniq by object attribute in Ruby

ruby-on-railsrubyarraysunique

提问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

Use Array#uniqwith a block:

使用Array#uniq带块:

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

回答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_byuniqsort_bysort。用法:

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