I'm trying to create a Hash
of Hash
-es. The value of the 2nd level hash is an integer defaulted to 0. What I'm trying to do is start with an empty hash of hashes, and then when I add a value if the key doesn't exist, they key should be added with a default value.
When I try this with a simple Hash
of integers, it works fine:
irb(main):003:0> h = Hash.new(0)
=> {}
irb(main):004:0> h[1] += 1
=> 1
irb(main):005:0> p h
{1=>1}
=> nil
irb(main):006:0> h.keys.size
=> 1
irb(main):007:0>
h
now has one key and a value of 1. Perfect.
But when my hash's values are hashes, it doesn't appear to work:
irb(main):007:0> h = Hash.new(Hash.new(0))
=> {}
irb(main):008:0> h[1][1] += 1
=> 1
irb(main):009:0> p h
{}
=> nil
irb(main):010:0> h.keys.size
=> 0
irb(main):011:0>
Am I doing something wrong, or can I not set the default value for a hash to Hash.new(0)
?
EDIT:
Based on the answers below, I was able to figure out what I was doing wrong. Actually, I was able to figure out where my thinking was faulty. Long story short, h[1][1]
doesn't nest Hash.new
calls, unless you give Hash.new
a block of code to call. The value of the expression h[1][1] += 1
is 1
as expected, but only the innermost hash was being initialized properly.
I'm posting this because while my example above uses a 2-dimensional hash, my actual problem uses a 3-dimensional hash:
syms[level][exchange][symbol] = count
The solution might be helpful to others having this problem, so here's the code that gets this working the way I want:
irb(main):024:0> syms = Hash.new{|h1,k1| h1[k1] = Hash.new{|h2,k2| h2[k2] = Hash.new(0)}}
=> {}
irb(main):026:0> syms["level1"]["NYSE"]["IBM"] += 1
=> 1
irb(main):027:0> p syms
{"level1"=>{"NYSE"=>{"IBM"=>1}}}
=> nil
irb(main):028:0>