开发者

Ruby: taking unique subarrays with respect to a specific field

Let's say t开发者_运维技巧here is an array of arrays of strings:

 array = [["John","Apples"],["Tim","Apples"],["Frank","Apples"],
  ["Tom","Pears"],["John","Pears"],["Frank","Oranges"],["Tim","Oranges"]]

Now the game is to select any records that are unique on the second value of the Arrays in an easy way, e.g. the result could be:

 array2 = [["Frank","Apples"],["Tom","Pears"],["Tim","Oranges"]]

Does anyone know if there's a one-liner that does this?


Array#uniq can take a block argument:

array.uniq { |e| e[1] }

For example:

>> array = [["John","Apples"], ["Tim","Apples"], ["Frank","Apples"], ["Tom","Pears"], ["John","Pears"], ["Frank","Oranges"], ["Tim","Oranges"]]
>> array.uniq { |e| e[1] }
=> [["John", "Apples"], ["Tom", "Pears"], ["Frank", "Oranges"]]

You'll probably get the first match (rather than the last as in your "could be" output) but I don't think there is any guarantee as to which one will be chosen.

Note that this only works in 1.9, 1.8 doesn't like it so you'll have to work harder in 1.8 but not that much harder:

array.inject({ }) { |h,e| h[e[1]] = e[0]; h }.map { |k,v| [ v, k ] }

The inject/map version works the same in 1.8 and 1.9. Also, this one picks the last of the duplicated values.


Another solution that should work in 1.8 and 1.9:

array.group_by(&:last).map { |k,v| v.last }
# => [["John", "Pears"], ["Frank", "Apples"], ["Tim", "Oranges"]]


In Ruby 1.8:

array.map{ |k,v| v }.uniq.map{ |uv| array.select{ |k,v| v == uv }.last }

or

Hash[*array.map{ |k,v| [v,k] }.flatten].map{ |k,v| [v,k] }

[updated: "mu is too short" gave an excellent answer for Ruby 1.9, and the above answers I gave are good for Ruby 1.8]


The facets gem implements (amongst a huge amount of other useful methods) a uniq_by method:

module Enumerable
  def uniq_by #:yield:
    h = {}; inject([]) {|a,x| h[yield(x)] ||= a << x}
  end
end
0

上一篇:

下一篇:

精彩评论

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

最新问答

问答排行榜