Why is :key.hash != 'key'.hash in Ruby?

I'm learning Ruby right now for the Rhodes mobile application framework and came across this problem: Rhodes' HTTP client parses JSON responses into Ruby data structures, e.g.

puts @params # prints {"body"=>{"results"=>[]}}

Since the key "body" is a string here, my first attempt @params[:body] failed (is nil) and instead it must be @params['body']. I find this most unfortunate.

Can somebody explain the rationale why strings and symbols have different hashes, i.e. :body.hash != 'body'.hash in this case?

Answers


Symbols and strings serve two different purposes.

Strings are your good old familiar friends: mutable and garbage-collectable. Every time you use a string literal or #to_s method, a new string is created. You use strings to build HTML markup, output text to screen and whatnot.

Symbols, on the other hand, are different. Each symbol exists only in one instance and it exists always (i.e, it is not garbage-collected). Because of that you should make new symbols very carefully (String#to_sym and :'' literal). These properties make them a good candidate for naming things. For example, it's idiomatic to use symbols in macros like attr_reader :foo.

If you got your hash from an external source (you deserialized a JSON response, for example) and you want to use symbols to access its elements, then you can either use HashWithIndifferentAccess (as others pointed out), or call helper methods from ActiveSupport:

require 'active_support/core_ext'

h = {"body"=>{"results"=>[]}}
h.symbolize_keys # => {:body=>{"results"=>[]}}
h.stringify_keys # => {"body"=>{"results"=>[]}}

Note that it'll only touch top level and will not go into child hashes.


Need Your Help

PreviewMouseLeftButtonUp event doesn't get called when mouse clicked

c# events button mouseevent

I have a listview in which I display objects with their state and name. The state is surrounded by a button and when I click on it the state changes from active to inactive or the other way round.