If you try to access a regular symbolized Hash in Ruby with a string key, it returns nil
value.
framework = { name: 'Ruby on Rails', language: 'Ruby' }
framework[:name] # "Ruby on Rails"
framework['language'] # nil
To design a better method API, you may want to allow the users (or method callers) to pass any key, without worrying whether it has to be a string or a symbol.
To access the Hash without worrying about the type of the key, use the ActiveSupport::HashWithIndifferentAccess
class provided in ActiveSupport library. It implements a hash where keys :name
and "name"
are considered to be the same.
require "active_support/hash_with_indifferent_access"
framework = ActiveSupport::HashWithIndifferentAccess.new
framework[:name] = 'Ruby on Rails'
puts framework[:name] # Ruby on Rails
puts framework['name'] # Ruby on Rails
You can also pass an existing Hash to the constructor to get a new instance:
require "active_support/hash_with_indifferent_access"
framework = { name: 'Ruby on Rails', language: 'Ruby' }
result = ActiveSupport::HashWithIndifferentAccess.new(framework)
puts result['name'] # Ruby on Rails
puts result[:language] # Ruby
If that long class name looks intimidating, don't worry, you don't have to initialize this class yourself. Instead, use the with_indifferent_access
extension method.
require "active_support/core_ext/hash/indifferent_access"
framework = { name: 'Ruby on Rails', language: 'Ruby' }
result = framework.with_indifferent_access
puts result[:name] # Ruby on Rails
puts result['language'] # Ruby
The with_indifferent_access
method is added by Active Support by monkey-patching the Hash
class. It returns a new instance of the ActiveSupport::HashWithIndifferentAccess
, initializing it with the current hash.
# lib/active_support/core_ext/hash/indifferent_access.rb
class Hash
def with_indifferent_access
ActiveSupport::HashWithIndifferentAccess.new(self)
end
end
This class supports all the existing hash API, so you can call any method you'd call on a regular Ruby hash.
A good real-world example is the params
hash in Rails controllers, which can be accessed with either a string or a symbol.
params = ActionController::Parameters.new(name: "Ruby")
params[:name] # Ruby
params['name'] # Ruby
Behind the scenes, it uses the with_indifferent_access
method as follows:
module ActionController
class Parameters
def initialize(parameters = {}, logging_context = {})
# ...
@parameters = parameters.with_indifferent_access
end
end
end
What about the performance?
If you're worried about performance, check out this Reddit post for a nuanced discussion: Why wouldn't you want a hash with indifferent access?
However, as the user stillness_still mentions:
I got 99 problems and the lookup time difference between a symbol and a string ain't one.
Well said, sir, well said.
That's a wrap. I hope you liked this article and you learned something new.
As always, if you have any questions or feedback, didn't understand something, or found a mistake, please leave a comment below or send me an email. I reply to all emails I get from developers, and I look forward to hearing from you.
If you'd like to receive future articles directly in your email, please subscribe to my blog. If you're already a subscriber, thank you.