LaunchKit
All quizzes
Ruby 1 views

Duplicate keys with Hash#compare_by_identity

Question

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

h = {}.compare_by_identity

h["key"] = 42
h["key"] = 84

h # => ???

The correct answer is

Explanation

TL;DR

compare_by_identity switches the hash from comparing keys with eql? and hash to comparing them with object identity (equal?). Each "key" literal allocates a new String object with its own object_id, so the two assignments create two distinct entries: {"key" => 42, "key" => 84}.

Step by step

Every occurrence of a string literal builds a fresh object:

"key".object_id == "key".object_id # => false

In a regular hash the two keys count as the same because they are eql? and share a hash value, so the second assignment overwrites the first. After compare_by_identity, only being the exact same object counts:

h = {}.compare_by_identity
h.compare_by_identity? # => true

h["key"] = 42
h["key"] = 84

h # => {"key" => 42, "key" => 84}

Lookups follow the same rule. A third "key" literal is yet another object, identical to neither stored key, so it finds nothing:

h = {}.compare_by_identity
h["key"] = 42

h["key"] # => nil

To hit an entry you must hold a reference to the very object used as the key:

h = {}.compare_by_identity
k = "key"
h[k] = 42

h[k] # => 42

Edge cases

Symbols and small integers are immediates: every occurrence is the same object, so they still collide in an identity hash:

h = {}.compare_by_identity
h[:key] = 1
h[:key] = 2

h # => {key: 2}

The # frozen_string_literal: true magic comment deduplicates identical string literals within a file, turning the two "key" literals into one object. The original snippet then behaves like a regular hash again and ends up as {"key" => 84}.

compare_by_identity is a one-way switch on the receiver: there is no method to turn it back off.

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.