ランキング
- ref:まめめも - ランキングの集計
とりあえず、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]}