Posted at

ハッシュを無限にネストする

More than 5 years have passed since last update.

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 は存在しないキーが渡される度に評価されます。そこで、この時にデフォルトでハッシュを代入するようなハッシュをデフォルト値として設定することで、無限ネストができることになります。