Best way to convert strings to symbols in hash

前端 未结 30 2971
借酒劲吻你
借酒劲吻你 2020-11-27 09:30

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.



        
相关标签:
30条回答
  • 2020-11-27 09:57

    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.

    0 讨论(0)
  • 2020-11-27 09:57

    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.

    0 讨论(0)
  • 2020-11-27 09:58

    Even more terse:

    Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
    
    0 讨论(0)
  • 2020-11-27 09:59

    a shorter one-liner fwiw:

    my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
    
    0 讨论(0)
  • 2020-11-27 09:59

    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

    0 讨论(0)
  • 2020-11-27 09:59

    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
      
    0 讨论(0)
提交回复
热议问题