在Ruby中,将哈希中的所有键从字符串转换为符号的(最快/最干净/直接)方法是什么?
这在解析YAML时非常方便。
my_hash = YAML.load_file('yml')
我希望能够使用:
my_hash[:key]
而不是:
my_hash['key']
在Ruby中,将哈希中的所有键从字符串转换为符号的(最快/最干净/直接)方法是什么?
这在解析YAML时非常方便。
my_hash = YAML.load_file('yml')
我希望能够使用:
my_hash[:key]
而不是:
my_hash['key']
当前回答
在ruby中,我发现这是最简单、最容易理解的将字符串键转换为符号的方法:
my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)}
对于散列中的每个键,我们调用delete函数将其从散列中删除(delete也返回与被删除的键相关的值),并立即将其设置为符号化的键。
其他回答
Symbolize_keys递归为任何哈希:
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
在Ruby >= 2.5 (docs)中,你可以使用:
my_hash.transform_keys(&:to_sym)
使用旧的Ruby版本?下面是一个单行程序,它将哈希复制到一个新的哈希中,并将键符号化:
my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
使用Rails,您可以使用:
my_hash.symbolize_keys
my_hash.deep_symbolize_keys
你可以偷懒,把它用lambda括起来:
my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }
my_lamb[:a] == my_hash['a'] #=> true
但这只适用于从散列中读取数据,而不是写入数据。
要做到这一点,你可以使用hash# merge
my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml'))
init块将按需一次转换键,但如果您在访问符号版本后更新键的字符串版本的值,则符号版本将不会更新。
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}
你也可以让init块不更新哈希,这将保护你免受这种错误,但你仍然容易受到相反的攻击-更新符号版本不会更新字符串版本:
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}
所以要注意的是在两种键形式之间切换。坚持用一个。
从Ruby 2.5.0开始,你可以使用哈希#transform_keys或哈希#transform_keys!
{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
这是一种深度象征物体的方法
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