loading...

Hash#new

scottw profile image Scott Watermasysk Originally published at scottw.com on ・1 min read

I was reading this post on Hash#fetch, and it reminded me of another lesser used/understood method on Ruby’s Hash object, new.

Typically, in Ruby, a hash is created by using just the {} literal. This is the equivalent of just doing Hash.new.

h1 = {}
h2 = Hash.new

Regardless of which option you choose, you get the same result.

However, as in most things Ruby, there is more than one way. The Hash initializer has three options:

  1. No parameters
  2. A default parameter
  3. A Block

Next to the literal, the one I use most often is option #2, a default parameter. Typically, I use this when I want to count a bunch of things.

a = [1,2,3,4,1,1,3]
h = Hash.new(0)
a.each {|i| h[i] += 1}
puts h #{1=>3, 2=>1, 3=>2, 4=>1}

Without the Hash.new(0) we would have to check for nil, or do an assignment like (h[i] ||= 0) += 1 which is much less readable and depending on what you are iterating over can get complicated.

Posted on by:

scottw profile

Scott Watermasysk

@scottw

Boostrapped Founder. In to Ruby, Rails, JavaScript, VSCode, React, Tailwind, and more.

Discussion

markdown guide
 

Also good to remember to be careful if you're trying to set your default value to an array, string, or hash. The default value passed in is just reused and mutated

h = Hash.new([])
=> {}
a.each {|i| h[i] <<= i}
=> [1, 2, 3, 4, 1, 1, 3]
h
=> {1=>[1, 2, 3, 4, 1, 1, 3], 2=>[1, 2, 3, 4, 1, 1, 3], 3=>[1, 2, 3, 4, 1, 1, 3], 4=>[1, 2, 3, 4, 1, 1, 3]}

Note that I had to use <<= to assign the value to the key, if you just do <<, h would still be an empty hash.

If I want the default value to be an array, I usually use the third option and pass a block Hash.new { [] }, which will give me a new array for each key.