nested_hash = Hash.new do |hash, key|
hash[key] = Hash.new(&hash.default_proc)
end
この nested_hash
はこのように好き勝手にハッシュの入れ子を作ることができます。
nested_hash[:a][:b][:c][:d][:e][:f][:g] = 1
#=> 1
nested_hash
#=> {:a=>{:b=>{:c=>{:d=>{:e=>{:f=>{:g=>1}}}}}}}
解説
ハッシュのデフォルト値
Rubyのハッシュは初期化時に Hash.new
にブロックを渡すことで、キーが無いときの初期値を設定することができます。
例えばユーザIDと得点が入った配列があるとします。
# [[user_id, score], ...]
scores = [[1, 4], [2, 3], [1, 3], [1, 3]]
この配列に対して各ユーザの合計点を計算する場合、例えば0に初期化されるハッシュを作ると簡単に計算できます。
score_hash = Hash.new { 0 }
scores.each { |user_id, score| score_hash[user_id] += score }
score_hash
#=> {1=>10, 2=>3}
そして、このデフォルトの動作は Hash#default_proc
で参照することができます。名前からも分かるように Proc
オブジェクトが返ります。
score_hash.default_proc.call
#=> 0
default_proc はハッシュ自身をブロック引数に渡される
hash = Hash.new { |h, k| hash == h }
hash[:unknown]
#=> true
そして Hash#default_proc
は存在しないキーが渡される度に評価されます。そこで、この時にデフォルトでハッシュを代入するようなハッシュをデフォルト値として設定することで、無限ネストができることになります。