开发者

Fastest/One-liner way to remove duplicates (by key) in Ruby Array?

What's the fa开发者_如何转开发stest/one-liner way to remove duplicates in an array of objects, based on a specific key:value, or a result returned from a method?

For instance, I have 20 XML Element nodes that are all the same name, but they have different "text" values, some of which are duplicates. I would like to remove the duplicates by saying "if element.text == previous_element.text, remove it". How do I do that in Ruby in the shortest amount of code?

I've seen how to do it for simple string/integer values, but not for objects.


Here's the standard hashy way. Note the use of ||= operator, which is a more convenient (a ||= b) way to write a = b unless a.

array.inject({}) do |hash,item|
   hash[item.text]||=item
   hash 
end.values.inspect

You can do it in a single line either.

The script needs O(n) equality checks of text strings. That's what's covered under O(n) when you see a hash.


This does it all:

Hash[*a.map{|x| [x.text, x]}].values

short? yep.

(asterisk is optional; seems to be required for 1.8.6).

For example:

a = [Thing.new('a'), Thing.new('b'), Thing.new('c'), Thing.new('c')]
=> [#<Thing a>, #<Thing b>, #<Thing c>, #<Thing c>]

Hash[a.map{|x| [x.text, x]}].values
=> [#<Thing a>, #<Thing b>, #<Thing c>]

Boring part: here's the little test class I used:

class Thing
  attr_reader :text
  def initialize(text)
    @text = text
  end

  def inspect
    "#<Thing #{text}>"
  end
end


Use Array#uniq with a block. In your case:

array.uniq(&:text) # => array with duplicated `text` removed

This was introduced in Ruby 1.9.2, so if using an earlier version, you can use backports with require 'backports/1.9.2/array/uniq'

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜