Am learning to code with ruby. I am learning about hashes and i dont understand this code: count = Hash.new(0)
. It says that the 0 is a default value, but when i run it on irb it gives me an empty hash {}. If 0 is a default value why can't i see something like count ={0=>0}
. Or is the zero an accumulator but doesn't go to the keys or values? Thanks

- 12,025
- 4
- 33
- 56

- 57
- 6
4 Answers
0 will be the fallback if you try to access a key in the hash that doesn't exist
For example:
count = Hash.new
-> count['key'] => nil
vs
count = Hash.new(0)
-> count['key'] => 0

- 138
- 4
-
1Might want to answer this part too: *"Or is the zero an accumulator but doesn't go to the keys or values?"* as it is an excellent question and inadvertently identifies a common ruby gotcha – engineersmnky Sep 02 '21 at 17:57
-
@engineersmnky can you rephrase what you interpret OP is asking? I didn't understand what he meant by asking if it was "an accumulator" / what the common ruby gotcha was – melcher Sep 02 '21 at 19:10
-
2@melcher There are two common gotchas with Hash defaults: (1) accidentally shared references with things like `Hash.new([])` so `h = Hash.new([]); h[:v].push(11)` has side effects; (2) no auto-vivification with `Hash.new(some_value)` so `h = Hash.new(0); puts h[:v]` gives you `0` but doesn't add `:v` as a key but it would if you started with `h = Hash.new { |h, k| h[k] = 0 }`. Not sure which one is meant though. – mu is too short Sep 02 '21 at 19:17
-
1@muistooshort yeah, I'm writing an answer to address those gotchas since I think it's worth repeating for a question like this, i just didn't really understand what was meant by 'accumulator'.... is that a reference to something like `inject`'s arguments? – melcher Sep 02 '21 at 19:30
-
1This answer is not especially useful as it casts no light on the question, "When should hashes be created with the form of [Hash::new](https://ruby-doc.org/core-2.7.0/Hash.html#method-c-new) that takes an argument and no block?". – Cary Swoveland Sep 03 '21 at 03:53
To expand on the answer from @jeremy-ramos and comment from @mu-is-too-short.
There are two common gotcha's with defaulting hash values in this way.
1. Accidentally shared references.
Ruby uses the exact same object in memory that you pass in as the default value for every missed key.
For an immutable object (like 0
), there is no problem. However you might want to write code like:
hash = Hash.new([])
hash[key] << value
or
hash = Hash.new({})
hash[key][second_key] = value
This will not do what you'd expect. Instead of hash[unknown_key]
returning a new, empty array or hash it will return the exact same array/hash object for every key.
so doing:
hash = Hash.new([])
hash[key1] << value1
hash[key2] << value2
results in a hash where key1
and key2
both point to the same array object containing [value1, value2]
Solution
To solve this you can create a hash with a default block argument instead (which is called whenever a missing key is accessed and lets you assign a value to the missed key)
hash = Hash.new{|h, key| h[key] = [] }
2. Assignment of missed keys with default values
When you access a missing key that returns the default value, you might expect that the hash will now contain that key with the value returned. It does not. Ruby does not modify the hash, it simply returns the default value. So, for example:
hash = Hash.new(0) #$> {}
hash.keys.empty? #$> true
hash[:foo] #$> 0
hash[:foo] == 0 #$> true
hash #$> {}
hash.keys.empty? #$> true
Solution
This confusion is also addressed using the block approach, where they keys value can be explicitly set.

- 1,543
- 9
- 15
-
2This is exactly where I was going with my comment under @jeremyramos's answer. The question in the OP was very insightful (whether or not it was intended to be) and I thought it deserved adequate attention. – engineersmnky Sep 02 '21 at 20:20
The Hash.new
docs are not very clear on this. I hope that the example below clarifies the difference and one of the frequent uses of Hash.new(0)
.
The first chunk of code uses Hash.new(0)
. The hash has a default value of 0, and when new keys are encountered, their value is 0. This method can be used to count the characters in the array.
The second chunk of code fails, because the default value for the key (when not assigned) is nil
. This value cannot be used in addition (when counting), and generates an error.
count = Hash.new(0)
puts "count=#{count}"
# count={}
%w[a b b c c c].each do |char|
count[char] += 1
end
puts "count=#{count}"
# count={"a"=>1, "b"=>2, "c"=>3}
count = Hash.new
puts "count=#{count}"
%w[a b b c c c].each do |char|
count[char] += 1
# Fails: in `block in <main>': undefined method `+' for nil:NilClass (NoMethodError)
end
puts "count=#{count}"
SEE ALSO:

- 12,024
- 2
- 30
- 47
TL;DR When you initialize hash using Hash.new
you can setup default value or default proc (the value that would be returned if given key does not exist)
Regarding the question to understand this magic firstly you need to know that Ruby hashes have default values. To access default value you can use Hash#default
method
This default value by default :) is nil
hash = {}
hash.default # => nil
hash[:key] # => nil
You can set default value with Hash#default=
hash = {}
hash.default = :some_value
hash[:key] # => :some_value
Very important note: it is dangerous to use mutable object as default because of side effect like this:
hash = {}
hash.default = []
hash[:key] # => []
hash[:other_key] << :some_item # will mutate default value
hash[:key] # => [:some_value]
hash.default # => [:some_value]
hash # => {}
To avoid this you can use Hash#default_proc
and Hash#default_proc=
methods
hash = {}
hash.default_proc # => nil
hash.default_proc = proc { [] }
hash[:key] # => []
hash[:other_key] << :some_item # will not mutate default value
hash[:other_key] # => [] # because there is no this key
hash[:other_key] = [:symbol]
hash[:other_key] << :some_item
hash[:other_key] # => [:symbol, :some_item]
hash[:key] # => [] # still empty array as default
Setting default
cancels default_proc
and vice versa
hash = {}
hash.default = :default
hash.default_proc = proc { :default_proc }
hash[:key] # => :default_proc
hash.default = :default
hash[:key] # => :default
hash.default_proc # => nil
Going back to Hash.new
When you pass argument to this method, you initialize default value
hash = Hash.new(0)
hash.default # => 0
hash.default_proc # => nil
When you pass block to this method, you initialize default proc
hash = Hash.new { 0 }
hash.default # => nil
hash[:key] # => 0

- 12,025
- 4
- 33
- 56