The following code was taken from a RubyTapas screencast, but it wasn't explained sufficiently for a person of my experience. Setting up a hash like this
config = Hash.new do |h,k|
h[k] = Hash.new(&h.default_proc)
end
allows one to set and access values in the following way
config[:production][:database][:adapter] = 'mysql'
puts config[:production][:database][:adapter] # => "mysql"
When I do
puts config.inspect
It reveals a nested hash.
{:production=>{:database=>{:adapter=>"mysql"}}}
However, if I modify the code (to just play around with it) to remove &h.default_proc
config = Hash.new do |h,k|
h[k] = Hash.new() #&h.default_proc removed from parameter
end
I get this error
undefined method `[]=' for nil:NilClass (NoMethodError)
I don't get what is happening with Hash.new(&h.default_proc), when, for example, I do this
config[:production][:database][:adapter] = 'mysql'
puts config[:production][:database][:adapter] # => "mysql"
Can you break down that, explaining in detail how the block &hdefault_proc
is working in this context. Please also explain if you can when the proc is getting called. Why is the proc necessary to make this work
config[:production][:database][:adapter] = 'mysql'
and how does the proc get called? I thought you have to do proc.call to actually call the proc.
Thanks in advance
Hashes are inherently unordered. Hashes provide amortized O(1) insertion and retrieval of elements by key, and that's it. If you need an ordered set of pairs, use an array of arrays.
A Hash is a dictionary-like collection of unique keys and their values. Also called associative arrays, they are similar to Arrays, but where an Array uses integers as its index, a Hash allows you to use any object type. Hashes enumerate their values in the order that the corresponding keys were inserted.
You are taking it the wrong way. &h
is not a meaningful unit in this context. It is &
attached to h.default_proc
. That is a proc h.default_proc
turned into a block. Similar thing applies to the splat operator *
. The associative precedence of &
and *
is always the last. The reason is clear: it does not make sense otherwise.
Suppose you have:
config = Hash.new() # or `{}`
then, whenever you access config
with an un-existing key, it will return nil
, so
config[:production][:database]
will return an error because the method []
is not defined on config[:production]
, which equals nil
. When you do:
config = Hash.new do |h,k|
h[k] = Hash.new()
end
then, it will be ensured that when you access config
with an un-existing key, it creates a new hash as a value. So
config[:production][:database]
will now be okay because the method []
is defined on config[:production]
, which equals the newly created hash {}
. But still,
config[:production][:database][:adapter]
will return an error because the method []
is not defined on config[:production][:database]
, which equals nil
. If you instead have:
config = Hash.new do |h,k|
h[k] = Hash.new(&h.default_proc)
end
then, that means that, when you access config
with an un-existing key like:
config[:production]
the hash that is created this time will have a default_proc
set to the same one as config
, which means that
config[:procuction][:database]
is not a hash that returns nil
to a non-existing key call, but a hash that returns another hash to a non-existing key call. So this time,
config[:production][:database][:adapter]
will not return an error. Similar thing happens with an arbitrary depth within the hash.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With