til: Ruby's hashmap syntax

March 2024

I was writing some Ruby for the first time, and I made a hashmap with some data in it.

map1 = {"foo": 1, "bar": 2}

I was then surprised to find out that I couldn't get any items out of it.

> map1 = {"foo": 1, "bar": 2}
> map1["foo"]
=> nil

Heck, it didn't even seem to have the key stored in it!

> map1 = {"foo": 1, "bar": 2}
> map1.has_key?("foo")
=> false

So I dug a bit into the documentation. The problem is there are two syntaxes for creating hashmap literals, a json-like syntax, and what Ruby calls the "rocket" syntax.

map1 = {"foo": 1, "bar": 2}     # the json-like syntax
map2 = {"foo" => 1, "bar" => 2} # "rocket" syntax

The json-like syntax is newer, and is the cause of my confusion. It silently converts each string key to a symbol.

> map1 = {"foo": 1, "bar": 2}
> map1.keys
=> [:foo, :bar]

The rocket syntax doesn't!

> map2 = {"foo" => 1, "bar" => 2}
> map2.keys
=> ["foo", "bar"]

So, for any maps created by the json-like syntax, they must be accessed with symbols.

> map1 = {"foo": 1, "bar": 2}
> map1[:foo]
=> 1

Or, just create them with the rocket syntax, and you can access them with strings. Like you expect.

I don't understand why this is the case. This conversion is surprising, especially since the json-like syntax is a newer addition to Ruby.

If you want to hear when I publish more, sign up for my mailing list!