13

I was getting an unexpected (to me) behavior in my code so I tried to isolate the problem in the REPL. However these constructors both appear to have the same result (an empty hash):

irb> a = {}
# => {}

irb> b = Hash.new(0)
# => {}

When I pass {} into a reduce function I get a NoMethodError, though. What is the difference between these two constructors?

irb> arr = "count the occurance of each of the words".scan(/\w+/)
# => ["count", "the", "occurance", "of", "each", "of", "the", "words"]

irb> x = arr.reduce(Hash.new(0)) { |hsh, word| hsh[word] += 1; hsh }
# => {"count"=>1, "the"=>2, "occurance"=>1, "of"=>2, "each"=>1, "words"=>1}

irb> x = arr.reduce({}) { |hsh, word| hsh[word] += 1; hsh }
# NoMethodError: undefined method `+' for nil:NilClass
doub1ejack
  • 10,627
  • 20
  • 66
  • 125
  • 3
    [Documentation](http://ruby-doc.org/core-1.9.3/Hash.html#method-c-new) isn't clear on this? "If obj is specified, this single object will be used for all default values." – Sergio Tulentsev Feb 03 '16 at 13:06
  • Downvote? That's a legit question. I'm 3 days into Ruby (and it's documentation) and no, I didn't find it immediately clear from the documentation. – doub1ejack Feb 03 '16 at 13:11
  • 1
    Even with the examples? – Sergio Tulentsev Feb 03 '16 at 13:25
  • Even so. Actually, earlier I assumed that there was some self-evident documentation I had missed, but looking more closely now I don't see where the `{}` constructor pattern is described or compared to `.new()`. Where is that? – doub1ejack Feb 03 '16 at 16:16

2 Answers2

33

Hash.new(0) sets default value for any key to 0, while {} sets nil

h1 = Hash.new(0)
h1.default  # => 0
h1[:a] += 1 # => 1
h2 = {}
h2.default  # => nil
h2[:a] += 1 # => NoMethodError: undefined method `+' for nil:NilClass
Rustam Gasanov
  • 15,290
  • 8
  • 59
  • 72
0

If we create a hash object using Hash.new(0), the parameter, 0 in this case, will be used as the hash’s default value—it will be the value returned if you look up a key that isn’t yet in the hash

def count_frequency(word_list)
  counts = Hash.new(0)
  word_list.each { |word|
    counts[word] += 1
  }
   counts
end
p count_frequency(%w(red blue green red white black))

will produce

{"red"=>2, "blue"=>1, "green"=>1, "white"=>1, "black"=>1}

Adding more to it

brands= Hash.new( "nike" )
puts "#{brands[0]}"
puts "#{brands[101]}"

both will produce

nike
nike

and

brands = {0 => "nike" , 1 =>"Reebok"}
brands[0]   # => "nike"
brands[1]   # => "Reebok"
brands[100] # => nil
Hassan Jamal
  • 694
  • 9
  • 11