Best way to convert strings to symbols in hash

前端 未结 30 3008
借酒劲吻你
借酒劲吻你 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.

提交回复
热议问题