LaunchKit
All quizzes
Ruby 1 views

Why Hash.new { [] } stays empty after <<

Question

What's the return value of the following Ruby code?

h = Hash.new { [] }

h[:a] << 42
h[:b] << 84

h # => ???

The correct answer is

Explanation

TL;DR

Hash.new { [] } runs the block on every missing-key lookup and returns the block's result, a brand-new empty array. The block never assigns anything into the hash, so h[:a] << 42 appends 42 to a throwaway array that is immediately discarded. No key is ever created and h returns {}.

Step by step

Each lookup of a missing key calls the block again and gets a different array:

h = Hash.new { [] }

h[:a]                # => []
h[:a].equal?(h[:a])  # => false

So the two appends each mutate a fresh array that nothing references afterward:

h = Hash.new { [] }

h[:a] << 42  # => [42]
h[:b] << 84  # => [84]

h            # => {}
h[:a]        # => []

The << calls return [42] and [84], proof that the append itself worked, but Hash#[] on a missing key only computes a default. It does not store it. The arrays become garbage as soon as the expression ends.

The working idiom

To build a hash of arrays, the block must take its two arguments (hash, key) and perform the assignment itself:

h = Hash.new { |hash, key| hash[key] = [] }

h[:a] << 42
h[:b] << 84

h # => {a: [42], b: [84]}

Here the first lookup of :a assigns the new array into the hash before returning it, so << mutates an array the hash actually holds.

Share this quiz

Comments

No comments yet. Be the first.

Only used to confirm and publish your comment. Never shown publicly, never shared.

Markdown: **bold**, `code`, ```fenced blocks```, > quotes, [links](url). HTML and images are not rendered.