Best way to convert strings to symbols in hash
In Ruby >= 2.5 (docs) you can use:
my_hash.transform_keys(&:to_sym)
Using older Ruby version? Here is a one-liner that will copy the hash into a new one with the keys symbolized:
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
With Rails you can use:
my_hash.symbolize_keys
my_hash.deep_symbolize_keys
How do I prevent string keys from being converted to symbols in Rails?
If you want that, the correct syntax is changing :
to =>
like this:
my_hash_arr = [{"name" => "DA", "amount" => 100000 }]
=> [{"name"=>"DA", "amount"=>100000}]
Unwanted symbol to string conversion of hash key
It may end up as a HashWithIndifferentAccess
if Rails somehow gets ahold of it, and that uses string keys internally. You might want to verify the class is the same:
assert_equal Hash, assigns(:my_hash).class
Parameters are always processed as the indifferent access kind of hash so you can retrieve using either string or symbol. If you're assigning this to your params hash on the get
or post
call, or you might be getting converted.
Another thing you can do is freeze it and see if anyone attempts to modify it because that should throw an exception:
@my_hash = { :my_key => :my_value }.freeze
Why would one use String keys for a hash over symbols
Because the source of the keys--the query string--is made up of strings, so searching through this string for keys, it is most directly convenient to index the hash via the strings.
Every Symbol that is created in the Ruby runtime is allocated and never released. There is a theoretical (but unlikely) DOS attack available by sending hundreds of thousands of requests with unique query string parameters. If these were symbolized, each request would slowly grow the runtime memory pool.
Strings, on the other hand, may be garbage collected. Thousands of unique strings handled across various requests will eventually go away, with no long-term impact.
Edit: Note that with Sinatra, symbols are also available for accessing the params
hash. However, this is done by creating a hash that is indexed by strings, and converting symbols (in your code) to strings when you make a request. Unless you do something like the following:
params.each{ |key,_| key.to_sym }
...you are not at risk of any symbol pseudo-DOS attack.
String as key being converted into symbol giving different behavior in rails
I'm not sure of any reason why the string keys would be "automatically" converted to symbols - probably there's some code in your application which does this. However you can deal with it pretty easily
for example you can use Hash#with_indifferent_access
# this will work even with symbol keys
on, direction = sort.with_indifferent_access.values_at "on", "direction"
Or you can use Hash#stringify_keys (there's also Hash#symbolize_keys for what it's worth, although you wouldn't need that here.)
on, direction = sort.stringify_keys.values_at "on", "direction"
If you were using plain Ruby (not Rails) and didn't want to include ActiveSupport (which provides with_indifferent_access
and stringify_keys
), there are options as well:
You could use Hash#transform_keys which used to be Rails-only, but is in Ruby core since 2.5:
on, direction = sort.transform_keys(&:to_s).values_at "on", "direction"
Or each_with_object / reduce
Hash key access via symbol not string
Because 'key'
is a String
and :key
is a Symbol
- those are two different things in Ruby.
It can be somewhat confusing, because :'key'
, or 'key':
will also be a Symbol
To make it work, just access Hash
fields with a Symbol
, like:
if (auth[:user])
To convert String indexed Hash to Symbol indexed Hash, refer to this question:
Best way to convert strings to symbols in hash
Rails using Symbol vs String as key in params hash
The reason used to be security. It's no longer relevant in Ruby 2.2 or later.
Before Ruby 2.2 symbols were not garbage collected. This means that once a symbol was created via a literal (:my_symbol
) or #to_sym
it would be around forever.
If Rails used symbols instead of strings then it would create symbols corresponding to names of params in a request. An attacker could send requests with params named param1
, param2
, ... and exhaust server memory by making the app allocate hundreds of thousands of symbols.
It's no longer the case in Ruby 2.2 or later
Why use symbols as hash keys in Ruby?
TL;DR:
Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.
Ruby Symbols are immutable (can't be changed), which makes looking something up much easier
Short(ish) answer:
Using symbols not only saves time when doing comparisons, but also saves memory, because they are only stored once.
Symbols in Ruby are basically "immutable strings" .. that means that they can not be changed, and it implies that the same symbol when referenced many times throughout your source code, is always stored as the same entity, e.g. has the same object id.
Strings on the other hand are mutable, they can be changed anytime. This implies that Ruby needs to store each string you mention throughout your source code in it's separate entity, e.g. if you have a string "name" multiple times mentioned in your source code, Ruby needs to store these all in separate String objects, because they might change later on (that's the nature of a Ruby string).
If you use a string as a Hash key, Ruby needs to evaluate the string and look at it's contents (and compute a hash function on that) and compare the result against the (hashed) values of the keys which are already stored in the Hash.
If you use a symbol as a Hash key, it's implicit that it's immutable, so Ruby can basically just do a comparison of the (hash function of the) object-id against the (hashed) object-ids of keys which are already stored in the Hash. (much faster)
Downside:
Each symbol consumes a slot in the Ruby interpreter's symbol-table, which is never released.
Symbols are never garbage-collected.
So a corner-case is when you have a large number of symbols (e.g. auto-generated ones). In that case you should evaluate how this affects the size of your Ruby interpreter.
Notes:
If you do string comparisons, Ruby can compare symbols just by comparing their object ids, without having to evaluate them. That's much faster than comparing strings, which need to be evaluated.
If you access a hash, Ruby always applies a hash-function to compute a "hash-key" from whatever key you use. You can imagine something like an MD5-hash. And then Ruby compares those "hashed keys" against each other.
Every time you use a string in your code, a new instance is created - string creation is slower than referencing a symbol.
Starting with Ruby 2.1, when you use frozen strings, Ruby will use the same string object. This avoids having to create new copies of the same string, and they are stored in a space that is garbage collected.
Long answers:
https://web.archive.org/web/20180709094450/http://www.reactive.io/tips/2009/01/11/the-difference-between-ruby-symbols-and-strings
http://www.randomhacks.net.s3-website-us-east-1.amazonaws.com/2007/01/20/13-ways-of-looking-at-a-ruby-symbol/
https://www.rubyguides.com/2016/01/ruby-mutability/
Related Topics
How to Switch to Older Versions of the Ruby/Rails Environment
How to Controller (Start/Kill) a Background Process (Server App) in Ruby
How to Use "Gets" on a Rake Task
Rails Convert String to Number
Convert String to Decimal Number in Ruby
Ruby on Rails and JSON Parser from Url
How to Validate Ssl Certificate Chain in Ruby with Net/Http
How to Remove/Disable Sign Up from Devise
How to Make Object Instance a Hash Key in Ruby
How to Read Lines from File into Array
Using Polymorphic Paths with Nested Associations
Error About Nokogiri While Capistrano Deployment on Ubuntu Server
Rails 3 Initializes Extremely Slow on Ruby 1.9.2
Where/How to Include Helper Methods for Capybara Integration Tests
Ruby: Put Request with JSON Body