ブログ「hp12c」の一問題(Ruby)

またまた Ruby ブログ「hp12c」からの問題(?)です。
melborne.github.io
さて、データ

data = <<EOS
player gameA gameB
Bob    20    56
Ross   68    33
Bob    78    55
Kent   90    15
Alice  84    79
Ross   10    15
Jimmy  80    31
Bob    12    36
Kent   88    43
Kent   12    33
Alice  90    32
Ross   67    77
Alice  56    92
Jimmy  33    88
Jimmy  11    87
EOS

から出力

player  gameA gameB total
Alice   230   203   433
Jimmy   124   206   330
Kent    190   91    281
Ross    145   125   270
Bob     110   147   257

を得よという問題です(totalで降順)。

元ブログでの回答はこちら。

require "csv"

class CSV
  def group_by(&blk)
    Hash[ super.map { |k, v| [k, CSV::Table.new(v)] } ]
  end
end

csv = CSV.new(data, col_sep:' ', headers:true, converters: :numeric, header_converters: :symbol)
scores_by_player = csv.group_by(&:first)
stat = scores_by_player.map do |(_, player), t|
  ab = [:gamea, :gameb].map { |e| t[e].inject(:+) }
  [player, *ab, ab.inject(:+)]
end
puts "%s\t%s\t%s\ttotal" % csv.headers
puts stat.sort_by{ |s| -s.last }.map { |line| "%s\t%d\t%d\t%d" % line }

標準添付ライブラリを使っているわけですね。しかし、メソッドのオーバーライドはさすがにちょっとという気がします。それに、コードが凝りすぎて自分には読みにくい感じ。

ライブラリを使わず、極ふつうに素直にやったらどうなるか、考えてみました。

header, *given = data.each_line.map(&:split)
ga, gb = Hash.new(0), Hash.new(0)
given.each do |name, a, b|
  ga[name] += a.to_i
  gb[name] += b.to_i
end

table = [header + ["total"]] +
        given.map(&:first).uniq.map {|n| [n, ga[n], gb[n], ga[n] + gb[n]]}
        .sort {|a, b| b[3] <=> a[3]}
puts table.map {|p, a, b, t| sprintf "%s\t%s\t%s\t%s", p, a, b, t}

結構めんどうですね。もっとうまくできますかね。