ランキング

とりあえず、N 位に同数のものがあるケースを無視するとだ、Hash#to_a と sort_by でチェーンするのが自然なんでは。

ary = %w(foo bar foo baz foo bar qux foo bnar qux quux)
n = 3

counts = Hash.new { 0 }
ary.each do |x|
   counts[x] += 1
end

counts.to_a.sort_by {|key, count| count}.reverse.each_with_index do |(key, count), index|
   break if index == n
   puts "#{i+1} #{key} #{count}"
end

で、多分関数型言語に毒されている人間は Hash じゃなくて sort -> group_by という手段をとる。

require 'rubygems'
gem 'activesupport'
require 'active_support'

ary = %w(foo bar foo baz foo bar qux foo bnar qux quux)
n = 3

ary.sort.group_by{|x| x}.map {|key, values| [key, values.length]}.sort_by {|key, length| length}.reverse.each_with_index do |x, index|
   break if index == n
   puts "#{index+1} #{x[0]} #{x[1]}"
end

ケースによっては Ruby じゃなくて shell でやるのが一番速かったり。

cat file | sort | uniq -c | sort -nr -k 1 | head -n 3

いや、でも正直な話、わかりやすく作るんなら適当にメソッドに分割した方が建設的な気もするし、この機能をメソッドにしてしまえばあまり気にしなくていいという話もある。

追記

ActiveSupport 的にメソッドを追加するならこうだろうか。

module Enumerable
   def take_while
      result = []
      each do |item|
         break unless yield item
         result << item
      end

      return result
   end
end

class Array
   def rank_top_n(n, &block)
      ary = sort(&block)
      return ary if ary.size <= n
      
      ary.take_while {|item| yield(item, ary[n-1]) <= 0 } 
   end
end

ary = %w(foo bar foo baz foo bar qux foo bnar qux quux)
n = 3

counts = Hash.new { 0 }
ary.each do |item|
   counts[item] += 1
end

p counts.to_a.rank_top_n(n){|a, b| b[1] <=> a[1]}