LaunchKit
All quizzes
Ruby 0 views

Why h.replace(h.invert) loses your keys

Question

What's the return value of this Ruby 3 code?

h = { a: 1, b: 2 }
h.replace(h.invert)

h[:a] # => ???

The correct answer is

Explanation

TL;DR

h.invert returns a new hash with keys and values swapped: { 1 => :a, 2 => :b }. h.replace then overwrites the contents of h in place with that inverted hash. The key :a no longer exists, so h[:a] returns nil.

Step by step

Hash#invert builds and returns a new hash; it never mutates the receiver:

h = { a: 1, b: 2 }

h.invert # => {1 => :a, 2 => :b}
h        # => {a: 1, b: 2}

Hash#replace is the mutator. It swaps the contents of h for the contents of its argument while keeping the same object:

h = { a: 1, b: 2 }
id = h.object_id

h.replace(h.invert) # => {1 => :a, 2 => :b}

h                 # => {1 => :a, 2 => :b}
h.object_id == id # => true

After the replacement, :a is no longer a key; it is now a value. Looking up a missing key with Hash#[] returns the hash's default, which is nil here:

h = { 1 => :a, 2 => :b }

h[:a] # => nil
h[1]  # => :a

Edge cases

Hash#invert keeps only the last pair when values collide, so an inversion can lose data even before replace gets involved:

{ a: 1, b: 1 }.invert # => {1 => :b}

If a missing key should be an error rather than a silent nil, use Hash#fetch: h.fetch(:a) raises KeyError and surfaces this kind of bug immediately.

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.