As a follow up to my first post I’d like to quickly run over the “default value” related features of Ruby’s Hash
and explain when to use them.
totals = Hash.new(0)
totals[:jeff] # => 0
totals[:ann] += 1 # => 1
Use this when:
Do not use this when:
scored = Hash.new([])
scored[:jeff] # => []
scored[:ann] << 'A+'
scored[:jeff] # => ['A+']
scored # => {}
💩
fizz_bang = Hash.new { |hash, key| hash[key] = fizz_and_or_bang(key) }
fizz_bang[3] # => 'Fizz'
fizz_bang[5] # => 'Bang'
Use this when:
Don’t user this when:
It’s possible to change the default_proc and default value. The last assignment will be what’s used.
h = Hash.new
h[:jeff] # => nil
h.default = 'default'
h[:jeff] # => 'default'
h.default_proc = -> (*) { 'default_proc' }
h[:jeff] # => 'default_proc'
h.default = nil
h[:jeff] # => nil
h # => {}
New Ruby programmers probably do something like this:
hash = {}
hash[:key] ||= 1
This works fine until some falsy values are introduced:
hash = {1 => nil, 2 => false}
hash[1] ||= 1
hash[2] ||= 2
hash # {1 => 1, 2 => 2}
😩
When using ||
your falsy values will be overwritten. This is most probably not your intention. Instead of that use Hash#fetch
.
hash = {1 => nil, 2 => false}
hash.fetch(1) { 1 }
hash.fetch(2) { 2 }
hash # {1 => nil, 2 => false}
Hash#fetch
is best used when:
It’s also great to ensure a key is present
Hash.new.fetch(:a_missing_key)
# KeyError: key not found: :a_missing_key
Don’t use it when:
Hash#fetch
also has a second notation for the default value:
Hash.new.fetch(:a_missing_key, 'default value') # => 'default value'
I only use the notation with a block. There are no real benefits to the other notation except maybe some performance difference in super simple cases. In most cases though, the block version will be the better choice. It will postpone the evaluation of the default value until it’s needed; and - at lest to me - it looks better. 😍
So, what do you think this will do?
Hash.new('default value') { |hash, key| hash[key] = key.to_s }
How about this?
Hash.new.fetch('default value') { 'value' }
👉 Try it yourself 👈, I found it very surprising 😃