How do I convert a Ruby hash so that all of its keys are symbols?

后端 未结 15 1792
不思量自难忘°
不思量自难忘° 2020-12-04 19:01

I have a Ruby hash which looks like:

{ \"id\" => \"123\", \"name\" => \"test\" }

I would like to convert it to:

{ :id         


        
相关标签:
15条回答
  • 2020-12-04 19:37
    hash = {"apple" => "banana", "coconut" => "domino"}
    Hash[hash.map{ |k, v| [k.to_sym, v] }]
    #=> {:apple=>"banana", :coconut=>"domino"}
    

    @mu is too short: Didn't see word "recursive", but if you insist (along with protection against non-existent to_sym, just want to remind that in Ruby 1.8 1.to_sym == nil, so playing with some key types can be misleading):

    hash = {"a" => {"b" => "c"}, "d" => "e", Object.new => "g"}
    
    s2s = 
      lambda do |h| 
        Hash === h ? 
          Hash[
            h.map do |k, v| 
              [k.respond_to?(:to_sym) ? k.to_sym : k, s2s[v]] 
            end 
          ] : h 
      end
    
    s2s[hash] #=> {:d=>"e", #<Object:0x100396ee8>=>"g", :a=>{:b=>"c"}}
    
    0 讨论(0)
  • 2020-12-04 19:37

    If you happen to be in Rails then you'll have symbolize_keys:

    Return a new hash with all keys converted to symbols, as long as they respond to to_sym.

    and symbolize_keys! which does the same but operates in-place. So, if you're in Rails, you could:

    hash.symbolize_keys!
    

    If you want to recursively symbolize inner hashes then I think you'd have to do it yourself but with something like this:

    def symbolize_keys_deep!(h)
      h.keys.each do |k|
        ks    = k.to_sym
        h[ks] = h.delete k
        symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
      end
    end
    

    You might want to play with the kind_of? Hash to match your specific circumstances; using respond_to? :keys might make more sense. And if you want to allow for keys that don't understand to_sym, then:

    def symbolize_keys_deep!(h)
      h.keys.each do |k|
        ks    = k.respond_to?(:to_sym) ? k.to_sym : k
        h[ks] = h.delete k # Preserve order even when k == ks
        symbolize_keys_deep! h[ks] if h[ks].kind_of? Hash
      end
    end
    

    Note that h[ks] = h.delete k doesn't change the content of the Hash when k == ks but it will preserve the order when you're using Ruby 1.9+. You could also use the [(key.to_sym rescue key) || key] approach that Rails uses in their symbolize_keys! but I think that's an abuse of the exception handling system.

    The second symbolize_keys_deep! turns this:

    { 'a' => 'b', 'c' => { 'd' => { 'e' => 'f' }, 'g' => 'h' }, ['i'] => 'j' }
    

    into this:

    { :a => 'b', :c => { :d => { :e => 'f' }, :g => 'h' }, ['i'] => 'j' }
    

    You could monkey patch either version of symbolize_keys_deep! into Hash if you really wanted to but I generally stay away from monkey patching unless I have very good reasons to do it.

    0 讨论(0)
  • 2020-12-04 19:37

    Just in case you are parsing JSON, from the JSON docs you can add the option to symbolize the keys upon parsing:

    hash = JSON.parse(json_data, symbolize_names: true)
    
    0 讨论(0)
  • 2020-12-04 19:40

    You can also extend core Hash ruby class placing a /lib/hash.rb file :

    class Hash
      def symbolize_keys_deep!
        new_hash = {}
        keys.each do |k|
          ks    = k.respond_to?(:to_sym) ? k.to_sym : k
          if values_at(k).first.kind_of? Hash or values_at(k).first.kind_of? Array
            new_hash[ks] = values_at(k).first.send(:symbolize_keys_deep!)
          else
            new_hash[ks] = values_at(k).first
          end
        end
    
        new_hash
      end
    end
    

    If you want to make sure keys of any hash wrapped into arrays inside your parent hash are symbolized, you need to extend also array class creating a "array.rb" file with that code :

    class Array
      def symbolize_keys_deep!
        new_ar = []
        self.each do |value|
          new_value = value
          if value.is_a? Hash or value.is_a? Array
            new_value = value.symbolize_keys_deep!
          end
          new_ar << new_value
        end
        new_ar
      end
    end
    

    This allows to call "symbolize_keys_deep!" on any hash variable like this :

    myhash.symbolize_keys_deep!
    
    0 讨论(0)
  • 2020-12-04 19:42

    I'm partial to:

    irb
    ruby-1.9.2-p290 :001 > hash = {"apple" => "banana", "coconut" => "domino"}
    {
          "apple" => "banana",
        "coconut" => "domino"
    }
    ruby-1.9.2-p290 :002 > hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
    {
          :apple => "banana",
        :coconut => "domino"
    }
    

    This works because we're iterating over the hash and building a new one on the fly. It isn't recursive, but you could figure that out from looking at some of the other answers.

    hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h }
    
    0 讨论(0)
  • 2020-12-04 19:42
    def symbolize_keys(hash)
       new={}
       hash.map do |key,value|
            if value.is_a?(Hash)
              value = symbolize_keys(value) 
            end
            new[key.to_sym]=value
       end        
       return new
    
    end  
    puts symbolize_keys("c"=>{"a"=>2,"k"=>{"e"=>9}})
    #{:c=>{:a=>2, :k=>{:e=>9}}}
    
    0 讨论(0)
提交回复
热议问题