Reason #24 • January 24th, 2026

Default Hash values

Back in the final example of #16 Enumerable#each_with_object, we could see Hash.new(0) used to create a Hash with a default value of 0. Let's explore it a bit further.

Default values are returned when accessing keys that don't yet exist in the Hash:

Ruby
counters = Hash.new(0)
counters["apples"] += 1
counters["bananas"] += 1
counters["apples"] += 1

puts counters
# => {"apples"=>2, "bananas"=>1}
      
JavaScript
const counters = {};
counters["apples"] = (counters["apples"] || 0) + 1;
counters["bananas"] = (counters["bananas"] || 0) + 1;
counters["apples"] = (counters["apples"] || 0) + 1;

console.log(counters);
// => { apples: 2, bananas: 1 }
      

If you want to use a mutable object (like an Array or another Hash) as the default value, you must use a block to ensure you generate a new object for each key:

Ruby
pokemon_abilities = Hash.new { |hash, key| hash[key] = [] }
pokemon_abilities["pikachu"] << "static"
pokemon_abilities["pikachu"] << "lightning-rod"
pokemon_abilities["bulbasaur"] << "overgrow"
pokemon_abilities["bulbasaur"] << "chlorophyll"

puts pokemon_abilities
# => {
#  "pikachu"=>["static", "lightning-rod"],
#  "bulbasaur"=>["overgrow", "chlorophyll"],
# }
      
JavaScript
const pokemonAbilities = {};

function addAbility(pokemon, ability) {
  if (!pokemonAbilities[pokemon]) {
    pokemonAbilities[pokemon] = [];
  }
  pokemonAbilities[pokemon].push(ability);
}

addAbility("pikachu", "static");
addAbility("pikachu", "lightning-rod");
addAbility("bulbasaur", "overgrow");
addAbility("bulbasaur", "chlorophyll");

console.log(pokemonAbilities);
// => {
//  pikachu: ["static", "lightning-rod"],
//  bulbasaur: ["overgrow", "chlorophyll"]
// }
      

I couldn't think of any cleaner approach in JavaScript than to create a dedicated method

A word of caution: if you fall into the trap of passing a mutable object as a default value without using a block, you'll end up with all keys sharing the same object instance:

Ruby
hash = Hash.new([])
hash["a"] << 1
hash["b"] << 2
puts hash
# => {}
puts hash["asdf"]
# => [1, 2]
    

😱

So long as you stay clear of this, using default values in Hashes is a powerful way to simplify your code!

History

Simple default values for Hashes were present in Ruby at least as early as version 1.6. The ability to use a block to generate default values was added in Ruby 1.8.

It seems likely that the Smalltalk Dictionary API at:ifAbsentPut: inspired this feature in Ruby, as it provides similar functionality for setting and retrieving a default value based on a block.