When symbolize_keys collides :key and "key"
Question
What's the return value of the following Rails 7 code?
h = { key: 42, 'key' => 84 }
h.symbolize_keys # => ???
The correct answer is
-
{ key: 84 }Correct -
{ key: 42 } -
{ "key" => 42 } -
{ "key" => 84 }
Explanation
TL;DR
Hash#symbolize_keys (Active Support) builds a new hash where every key that responds to to_sym is converted to a symbol. Both :key and "key" map to the symbol :key, and a hash cannot hold the same key twice. The pairs are copied in insertion order, so "key" => 84 is written after key: 42 and overwrites it: the result is { key: 84 }.
Step by step
The snippets below run in any Ruby with Active Support loaded (require "active_support/all"), which is what a Rails app does for you.
require "active_support/all"
h = { key: 42, "key" => 84 }
h.symbolize_keys # => {key: 84}
h # => {key: 42, "key" => 84}
symbolize_keys walks the pairs in insertion order and inserts each converted pair into a fresh hash:
:key => 42is inserted as:key => 42."key" => 84converts to:key => 84, which overwrites the existing:keyentry.
The receiver is untouched; symbolize_keys! is the in-place variant.
Insertion order decides the winner. Flip the pairs and the symbol key wins instead:
require "active_support/all"
{ "key" => 84, key: 42 }.symbolize_keys # => {key: 42}
Edge cases
symbolize_keys is shallow. Nested hashes keep their string keys; deep_symbolize_keys converts all levels:
require "active_support/all"
{ a: { "b" => 1 } }.symbolize_keys # => {a: {"b" => 1}}
{ "a" => { "b" => 1 } }.deep_symbolize_keys # => {a: {b: 1}}
In plain Ruby (2.5+), transform_keys does the same job without Active Support, with the same last-one-wins collision rule:
{ key: 42, "key" => 84 }.transform_keys(&:to_sym) # => {key: 84}
Share this quiz
Comments
No comments yet. Be the first.