Last active
December 15, 2015 15:09
-
-
Save pjb3/5279741 to your computer and use it in GitHub Desktop.
Default values in Hashes
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# The inner Hash.new(0) will now be the object that returned | |
# when you try to fetch the value for a key that doesn't exist | |
>> h = Hash.new(Hash.new(0)) | |
=> {} | |
# When you do this, you are mutating the object that is the default value | |
# of the Hash and not changing the Hash itself | |
>> h[:foo][10] += 1 | |
=> 1 | |
# The previous statement you can think of more logically like this: | |
>> h.default[10] += 1 | |
=> {10=>1} | |
# In other words, give me a reference to the object that is the default value | |
# for the Hash, and fetch that value of the key 10 in that Hash | |
# (which is not the original outer Hash), increment it, and assign it | |
# back to the key of 10 in that Hash. | |
# The point being that the same single object that was created when you first | |
# called Hash.new(0) is returned for every missing key, | |
# not a new Hash.new(0) for each call to a missing key | |
# To fix that problem, you want is to use the block form of Hash.new, | |
# which means return the result of evaluating this block each time you | |
# try to fetch the value for a key that doesn't exist, | |
# which will generate a new Hash.new(0) each time | |
>> h = Hash.new { Hash.new(0) } | |
=> {} | |
>> h[:foo][10] += 1 | |
=> 1 | |
>> h[:foo][10] += 1 | |
=> 1 | |
# But the problem now is that it generates a new Hash.new(0) each time, | |
# you mutate it, but that reference isn't assigned to anything, | |
# so you want really want is this: | |
>> h[:foo] = { 10 => (h[:foo][10] += 1) } | |
=> {10=>1} | |
>> h[:foo] = { 10 => (h[:foo][10] += 1) } | |
=> {10=>2} | |
>> h[:bar] = { 10 => (h[:bar][10] += 1) } | |
=> {10=>1} | |
# But that's too much typing and duplication, so you can just do | |
# the assignment in the block when you create the Hash in the first place: | |
>> hash = Hash.new {|h,k| | |
puts "no value for #{k.inspect} in #{h.inspect} yet" | |
h[k] = Hash.new(0) } | |
=> {} | |
# In this block, h is the Hash and k is the key you tried to access | |
# that didn't exist in the hash. You create a new Hash with a default value | |
# of 0 and assign that to the key k in the Hash h. | |
# I added a puts statement in there so you can see when the block is called | |
# On the first call, there is no value for :foo, | |
# so the block is called, which assigns a value to :foo | |
>> hash[:foo][10] += 1 | |
no value for :foo in {} yet | |
=> 1 | |
# Now there is a value for :foo, so that value is returned | |
# and you are then just mutating that object, the default block is not called | |
>> hash[:foo][10] += 1 | |
=> 2 | |
# When you use a different key, the block is called again | |
>> hash[:bar][10] += 1 | |
no value for :bar in {:foo=>{10=>2}} yet | |
=> 1 |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment