Radek - 1 year ago 57
Ruby Question

# How to assign hash["a"]["b"]= "c" if hash["a"] doesn't exist?

is there any other simpler way than

``````if (hash.has_key?("a") )
hash["a"]["b"] = "c"
else
hash["a"] = Hash.new
hash["a"]["b"] = "c"
end
``````

The easiest way is to construct your Hash with a block argument:

``````hash = Hash.new { |h, k| h[k] = { } }
hash['a']['b'] = 1
hash['a']['c'] = 1
hash['b']['c'] = 1
puts hash.inspect
# "{"a"=>{"b"=>1, "c"=>1}, "b"=>{"c"=>1}}"
``````

This form for `new` creates a new empty Hash as the default value. You don't want this:

``````hash = Hash.new({ })
``````

as that will use the exact same hash for all default entries.

Also, as Phrogz notes, you can make the auto-vivified hashes auto-vivify using `default_proc`:

``````hash = Hash.new{ |h,k| h[k] = Hash.new(&h.default_proc) }
``````

UPDATE: I think I should clarify my warning against `Hash.new({ })`. When you say this:

``````h = Hash.new({ })
``````

That's pretty much like saying this:

``````h = Hash.new
h.default = { }
``````

And then, when you access `h` to assign something as `h[:k][:m] = y`, it behaves as though you did this:

``````if(h.has_key?(:k))
h[:k][:m] = y
else
h.default[:m] = y
end
``````

And then, if you `h[:k2][:n] = z`, you'll end up assigning `h.default[:n] = z`. Note that `h` still says that `h.has_key?(:k)` is false.

However, when you say this:

``````h = Hash.new(0)
``````

Everything will work out okay because you will never modified `h[k]` in place here, you'll only read a value from `h` (which will use the default if necessary) or assign a new value to `h`.

Recommended from our users: Dynamic Network Monitoring from WhatsUp Gold from IPSwitch. Free Download