Best way to convert strings to symbols in hash

RubyHashmap

Ruby Problem Overview


What's the (fastest/cleanest/straightforward) way to convert all keys in a hash from strings to symbols in Ruby?

This would be handy when parsing YAML.

my_hash = YAML.load_file('yml')

I'd like to be able to use:

my_hash[:key] 

Rather than:

my_hash['key']

Ruby Solutions


Solution 1 - Ruby

Here's a better method, if you're using Rails:

params.symbolize_keys

The end.

If you're not, just rip off their code (it's also in the link):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end

Solution 2 - Ruby

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 

Solution 3 - Ruby

For the specific case of YAML in Ruby, if the keys begin with ':', they will be automatically interned as symbols.

require 'yaml'
require 'pp'
yaml_str = "
connections:

  • host: host1.example.com port: 10000
  • host: host2.example.com port: 20000 " yaml_sym = " :connections:
  • :host: host1.example.com :port: 10000
  • :host: host2.example.com :port: 20000 " pp yaml_str = YAML.load(yaml_str) puts yaml_str.keys.first.class pp yaml_sym = YAML.load(yaml_sym) puts yaml_sym.keys.first.class

Output:

/opt/ruby-1.8.6-p287/bin/ruby ~/test.rb

{"connections"=> [{"port"=>10000, "host"=>"host1.example.com"}, {"port"=>20000, "host"=>"host2.example.com"}]} String {:connections=> [{:port=>10000, :host=>"host1.example.com"}, {:port=>20000, :host=>"host2.example.com"}]} Symbol

Solution 4 - Ruby

if you're using Rails, it is much simpler - you can use a HashWithIndifferentAccess and access the keys both as String and as Symbols:

my_hash.with_indifferent_access 

see also:

http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


Or you can use the awesome "Facets of Ruby" Gem, which contains a lot of extensions to Ruby Core and Standard Library classes.

  require 'facets'
  > {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys
    =>  {:some=>"thing", :foo=>"bar}

see also: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash

Solution 5 - Ruby

Even more terse:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]

Solution 6 - Ruby

Since Ruby 2.5.0 you can use Hash#transform_keys or Hash#transform_keys!.

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}

Solution 7 - Ruby

http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }

Solution 8 - Ruby

Here's a way to deep symbolize an object

def symbolize(obj)
    return obj.inject({}){|memo,(k,v)| memo[k.to_sym] =  symbolize(v); memo} if obj.is_a? Hash
    return obj.inject([]){|memo,v    | memo           << symbolize(v); memo} if obj.is_a? Array
    return obj
end

Solution 9 - Ruby

If you are using json, and want to use it as a hash, in core Ruby you can do it:

json_obj = JSON.parse(json_str, symbolize_names: true)

symbolize_names: If set to true, returns symbols for the names (keys) in a JSON object. Otherwise strings are returned. Strings are the default.

Doc: Json#parse symbolize_names

Solution 10 - Ruby

I really like the Mash gem.

you can do mash['key'], or mash[:key], or mash.key

Solution 11 - Ruby

params.symbolize_keys will also work. This method turns hash keys into symbols and returns a new hash.

Solution 12 - Ruby

A modification to @igorsales answer

class Object
  def deep_symbolize_keys
    return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash
    return self.inject([]){|memo,v    | memo           << v.deep_symbolize_keys; memo} if self.is_a? Array
    return self
  end
end

Solution 13 - Ruby

In Rails you can use:

{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

Converts to:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}

Solution 14 - Ruby

So many answers here, but the one method rails function is hash.symbolize_keys

Solution 15 - Ruby

This is my one liner for nested hashes

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end

Solution 16 - Ruby

In case the reason you need to do this is because your data originally came from JSON, you could skip any of this parsing by just passing in the :symbolize_names option upon ingesting JSON.

No Rails required and works with Ruby >1.9

JSON.parse(my_json, :symbolize_names => true)

Solution 17 - Ruby

You could be lazy, and wrap it in a lambda:

my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }

my_lamb[:a] == my_hash['a'] #=> true

But this would only work for reading from the hash - not writing.

To do that, you could use Hash#merge

my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))

The init block will convert the keys one time on demand, though if you update the value for the string version of the key after accessing the symbol version, the symbol version won't be updated.

irb> x = { 'a' => 1, 'b' => 2 }
#=> {"a"=>1, "b"=>2}
irb> y = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(x)
#=> {"a"=>1, "b"=>2}
irb> y[:a]  # the key :a doesn't exist for y, so the init block is called
#=> 1
irb> y
#=> {"a"=>1, :a=>1, "b"=>2}
irb> y[:a]  # the key :a now exists for y, so the init block is isn't called
#=> 1
irb> y['a'] = 3
#=> 3
irb> y
#=> {"a"=>3, :a=>1, "b"=>2}

You could also have the init block not update the hash, which would protect you from that kind of error, but you'd still be vulnerable to the opposite - updating the symbol version wouldn't update the string version:

irb> q = { 'c' => 4, 'd' => 5 }
#=> {"c"=>4, "d"=>5}
irb> r = Hash.new { |h,k| h[k.to_s] }.merge(q)
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called
#=> 4
irb> r
#=> {"c"=>4, "d"=>5}
irb> r[:c] # init block is called again, since this key still isn't in r
#=> 4
irb> r[:c] = 7
#=> 7
irb> r
#=> {:c=>7, "c"=>4, "d"=>5}

So the thing to be careful of with these is switching between the two key forms. Stick with one.

Solution 18 - Ruby

Would something like the following work?

new_hash = Hash.new
my_hash.each { |k, v| new_hash[k.to_sym] = v }

It'll copy the hash, but you won't care about that most of the time. There's probably a way to do it without copying all the data.

Solution 19 - Ruby

a shorter one-liner fwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }

Solution 20 - Ruby

How about this:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))

# my_hash['key'] => "val"
# my_hash[:key]  => "val"

Solution 21 - Ruby

This is for people who uses mruby and do not have any symbolize_keys method defined:

class Hash
  def symbolize_keys!
    self.keys.each do |k|
      if self[k].is_a? Hash
        self[k].symbolize_keys!
      end
      if k.is_a? String
        raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym]
        self[k.to_sym] = self[k]
        self.delete(k)
      end
    end
    return self
  end
end

The method:

  • symbolizes only keys that are String
  • if symbolize a string means to lose some informations (overwrite part of hash) raise a RuntimeError
  • symbolize also recursively contained hashes
  • return the symbolized hash
  • works in place!

Solution 22 - Ruby

The array we want to change.

strings = ["HTML", "CSS", "JavaScript", "Python", "Ruby"]

Make a new variable as an empty array so we can ".push" the symbols in.

symbols = [ ]

Here's where we define a method with a block.

strings.each {|x| symbols.push(x.intern)}

End of code.

So this is probably the most straightforward way to convert strings to symbols in your array(s) in Ruby. Make an array of strings then make a new variable and set the variable to an empty array. Then select each element in the first array you created with the ".each" method. Then use a block code to ".push" all of the elements in your new array and use ".intern or .to_sym" to convert all the elements to symbols.

Symbols are faster because they save more memory within your code and you can only use them once. Symbols are most commonly used for keys in hash which is great. I'm the not the best ruby programmer but this form of code helped me a lot.If anyone knows a better way please share and you can use this method for hash too!

Solution 23 - Ruby

If you would like vanilla ruby solution and as me do not have access to ActiveSupport here is deep symbolize solution (very similar to previous ones)

    def deep_convert(element)
      return element.collect { |e| deep_convert(e) } if element.is_a?(Array)
      return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash)
      element
    end

Solution 24 - Ruby

Starting on Psych 3.0 you can add the symbolize_names: option

`Psych.load("---\n foo: bar")

=> {"foo"=>"bar"}`

`Psych.load("---\n foo: bar", symbolize_names: true)

=> {:foo=>"bar"}`

Note: if you have a lower Psych version than 3.0 symbolize_names: will be silently ignored.

My Ubuntu 18.04 includes it out of the box with ruby 2.5.1p57

Solution 25 - Ruby

ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2}
 => {"aaa"=>1, "bbb"=>2} 
ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}]
 => {:aaa=>1, :bbb=>2}

Solution 26 - Ruby

This is not exactly a one-liner, but it turns all string keys into symbols, also the nested ones:

def recursive_symbolize_keys(my_hash)
  case my_hash
  when Hash
    Hash[
      my_hash.map do |key, value|
        [ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ]
      end
    ]
  when Enumerable
    my_hash.map { |value| recursive_symbolize_keys(value) }
  else
    my_hash
  end
end

Solution 27 - Ruby

I like this one-liner, when I'm not using Rails, because then I don't have to make a second hash and hold two sets of data while I'm processing it:

my_hash = { "a" => 1, "b" => "string", "c" => true }

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key) }

my_hash
=> {:a=>1, :b=>"string", :c=>true}

Hash#delete returns the value of the deleted key

Solution 28 - Ruby

Facets' Hash#deep_rekey is also a good option, especially:

  • if you find use for other sugar from facets in your project,
  • if you prefer code readability over cryptical one-liners.

Sample:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey

Solution 29 - Ruby

In ruby I find this to be the most simple and easy to understand way to turn string keys in hashes to symbols :

my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}

For each key in the hash we call delete on it which removes it from the hash (also delete returns the value associated with the key that was deleted) and we immediately set this equal to the symbolized key.

Solution 30 - Ruby

Similar to previous solutions but written a bit differently.

  • This allows for a hash that is nested and/or has arrays.

  • Get conversion of keys to a string as a bonus.

  • Code does not mutate the hash been passed in.

     module HashUtils
       def symbolize_keys(hash)
         transformer_function = ->(key) { key.to_sym }
         transform_keys(hash, transformer_function)
       end
    
       def stringify_keys(hash)
         transformer_function = ->(key) { key.to_s }
         transform_keys(hash, transformer_function)
       end
    
       def transform_keys(obj, transformer_function)
         case obj
         when Array
           obj.map{|value| transform_keys(value, transformer_function)}
         when Hash
           obj.each_with_object({}) do |(key, value), hash|
             hash[transformer_function.call(key)] = transform_keys(value, transformer_function)
           end
         else
           obj
         end
       end
     end
    

Solution 31 - Ruby

symbolize_keys recursively for any hash:

class Hash
  def symbolize_keys
    self.is_a?(Hash) ? Hash[ self.map { |k,v| [k.respond_to?(:to_sym) ? k.to_sym : k, v.is_a?(Hash) ? v.symbolize_keys : v] } ] : self
  end
end

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionBryan M.View Question on Stackoverflow
Solution 1 - RubySaiView Answer on Stackoverflow
Solution 2 - RubySarah MeiView Answer on Stackoverflow
Solution 3 - RubyjrgmView Answer on Stackoverflow
Solution 4 - RubyTiloView Answer on Stackoverflow
Solution 5 - RubyMichael BartonView Answer on Stackoverflow
Solution 6 - RubySagar PandyaView Answer on Stackoverflow
Solution 7 - RubyEryView Answer on Stackoverflow
Solution 8 - RubyigorsalesView Answer on Stackoverflow
Solution 9 - RubyMarkView Answer on Stackoverflow
Solution 10 - RubyykaganovichView Answer on Stackoverflow
Solution 11 - RubyJae ChoView Answer on Stackoverflow
Solution 12 - RubyTonyView Answer on Stackoverflow
Solution 13 - RubyJayJayView Answer on Stackoverflow
Solution 14 - RubyshakirthowView Answer on Stackoverflow
Solution 15 - RubyNick DobsonView Answer on Stackoverflow
Solution 16 - RubyAdam GrantView Answer on Stackoverflow
Solution 17 - RubyrampionView Answer on Stackoverflow
Solution 18 - RubyChrisInEdmontonView Answer on Stackoverflow
Solution 19 - RubysensadromeView Answer on Stackoverflow
Solution 20 - RubyFredrik BoströmView Answer on Stackoverflow
Solution 21 - RubyMatteo RagniView Answer on Stackoverflow
Solution 22 - Rubyrubyguest123View Answer on Stackoverflow
Solution 23 - RubyHaris KrajinaView Answer on Stackoverflow
Solution 24 - RubyRodrigo EstebanezView Answer on Stackoverflow
Solution 25 - RubyVlad KhomichView Answer on Stackoverflow
Solution 26 - RubyCjoergView Answer on Stackoverflow
Solution 27 - Rubynevets138View Answer on Stackoverflow
Solution 28 - RubySergikonView Answer on Stackoverflow
Solution 29 - Rubyuser4488109View Answer on Stackoverflow
Solution 30 - RubyjhamView Answer on Stackoverflow
Solution 31 - RubyJakub ZakView Answer on Stackoverflow