Hash を使ったよくある集計

メモ。あるオブジェクトの集合があって、各オブジェクトの属性を集計したいとかそういう場合。
基本的に、Hash を使って、key-value 型でやればいい。

# -*- coding: utf-8 -*-
if RUBY_VERSION < "1.8.7"
  require 'enumerator'
end

Club = Class.new do
  attr_reader :_id_
  
  def self.find(*args)
    [new(1), new(2)]
  end
  
  def initialize(_id_)
    @_id_ = _id_
  end
end

Student = Struct.new(:name, :age, :height, :club_id)

students = %w[ 
  largo
  forte
  regret
  arietta
  air
].enum_with_index.map do |name, offset|
  Student.new(name, 10 + offset * 2, 120 + offset * 5, offset % 2 + 1)
end

# Hash を使う
total = {
  :age    => 0,
  :height => 0,
}

students.each do |s|
  total[:age] += s.age
  total[:height] += s.height
end

total # => {:age=>70, :height=>650}

# Club 単位での集計
total = {}
Club.find(:all).each do |club|
  total[club._id_] = {
    :age    => 0,
    :height => 0,
  }
end

students.each do |s|
  total[s.club_id][:age] += s.age
  total[s.club_id][:height] += s.height
end

total # => {1=>{:age=>42, :height=>390}, 2=>{:age=>28, :height=>260}}

# 集計にStruct を使うというのもある。アクセサが使える
total = Struct.new(:age, :height).new(0, 0)
students.each do |s|
  total.age += s.age
  total.height += s.height
end

total.age # => 70
total.height # => 650