如何转换Ruby哈希,使其所有的关键是符号?

我有一个Ruby哈希,如下所示:

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

我想将其转换为:

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

 hash = {"apple" => "banana", "coconut" => "domino"} Hash[hash.map{ |k, v| [k.to_sym, v] }] #=> {:apple=>"banana", :coconut=>"domino"} 

更新:

@mu太短了:没有看到“recursion”这个词,但是如果你坚持(同时保护不存在的to_sym ,只是想提醒一下,在Ruby 1.8 1.to_sym == nil ,所以使用一些键types可以误导):

 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"}} 

如果你碰巧在Rails中,那么你将有symbolize_keys

只要它们响应to_sym ,就返回一个新的散列,所有的键都被转换为符号。

symbolize_keys! 这是相同的,但在原地运作。 所以,如果你在Rails中,你可以:

 hash.symbolize_keys! 

如果你想recursion地表示内部哈希值,那么我认为你必须自己做,但是这样的东西:

 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 

你可能想玩的kind_of? Hash kind_of? Hash符合您的具体情况; 使用respond_to? :keys respond_to? :keys可能会更有意义。 如果你想允许不理解to_sym键,那么:

 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 

请注意, h[ks] = h.delete k不会在k == ks时更改哈希的内容,但在您使用Ruby 1.9+时,它将保持顺序。 您也可以使用[(key.to_sym rescue key) || key] Rails在symbolize_keys!使用的方法 但我认为这是滥用exception处理系统。

第二个symbolize_keys_deep! 变成这样:

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

进入这个:

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

你可以猴子修补symbolize_keys_deep!版本symbolize_keys_deep! 哈希如果你真的想要,但我通常远离猴子补丁,除非我有很好的理由去做。

如果您使用Rails> = 4,则可以使用:

 hash.deep_symbolize_keys hash.deep_symbolize_keys! 

要么

 hash.deep_stringify_keys hash.deep_stringify_keys! 

引用

以防万一你正在parsingJSON,从JSON文档你可以添加选项来parsing时符号键:

 hash = JSON.parse(json_data, symbolize_names: true) 

Victor Moroz为简单的recursion情况提供了一个可爱的答案,但它不会处理嵌套在嵌套数组中的哈希:

 hash = { "a" => [{ "b" => "c" }] } s2s[hash] #=> {:a=>[{"b"=>"c"}]} 

如果你需要在哈希内的数组中支持哈希,你会想要更类似于这样的东西:

 def recursive_symbolize_keys(h) case h when Hash Hash[ h.map do |k, v| [ k.respond_to?(:to_sym) ? k.to_sym : k, recursive_symbolize_keys(v) ] end ] when Enumerable h.map { |v| recursive_symbolize_keys(v) } else h end end 

尝试这个:

 hash = {"apple" => "banana", "coconut" => "domino"} # => {"apple"=>"banana", "coconut"=>"domino"} hash.tap do |h| h.keys.each { |k| h[k.to_sym] = h.delete(k) } end # => {:apple=>"banana", :coconut=>"domino"} 

这遍历了密钥,并且对于每个密钥都删除string化的密钥并将其值赋给符号化的密钥。

如果您使用的是Rails(或者只是主动支持):

 { "id" => "123", "name" => "test" }.symbolize_keys 

ruby单线速度比select的答案快

 hash = {"apple" => "banana", "coconut" => "domino"} #=> {"apple"=>"banana", "coconut"=>"domino"} hash.inject({}){|h,(k,v)| h[k.intern] = v; h} #=> {:apple=>"banana", :coconut=>"domino"} 

基准testing结果

 n = 100000 Benchmark.bm do |bm| bm.report { n.times { hash.inject({}){|h,(k,v)| h[k.intern] = v; h} } } bm.report { n.times { Hash[hash.map{ |k, v| [k.to_sym, v] }] } } end # => user system total real # => 0.100000 0.000000 0.100000 ( 0.107940) # => 0.120000 0.010000 0.130000 ( 0.137966) 

我偏向于:

 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" } 

这是有效的,因为我们正在迭代哈希,并build立一个新的dynamic。 这不是recursion的,但你可以从其他答案中看出来。

 hash.inject({}){ |h, (n,v)| h[n.to_sym] = v; h } 

你也可以扩展核心哈希ruby类放置一个/lib/hash.rb文件:

 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 

如果你想要确保包含在你的父哈希中的数组的哈希符号被符号化了,你还需要扩展数组类来创build一个包含该代码的“array.rb”文件:

 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 

这允许调用“symbolize_keys_deep!” 在任何像这样的哈希variables:

 myhash.symbolize_keys_deep! 
 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}}} 

这是我的两分钱,

我的symbolize_keys_deep!版本symbolize_keys_deep! 使用原始symbolize_keys! 由轨道提供,只是一个简单的recursion调用Symbolize子哈希值。

  def symbolize_keys_deep!(h) h.symbolize_keys! h.each do |k, v| symbolize_keys_deep!(v) if v.is_a? Hash end end 

Facets的Hash#rekey也是值得一提的。

样品:

 require 'facets/hash/rekey' { "id" => "123", "name" => "test" }.deep_rekey => {:id=>"123", :name=>"test"} 

还有一个recursion版本:

 require 'facets/hash/deep_rekey' { "id" => "123", "name" => {"first" => "John", "last" => "Doe" } }.deep_rekey => {:id=>"123", :name=>{:first=>"John", :last=>"Doe"}} 

这里有一个小小的recursion函数来对键进行深度符号化:

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

从Ruby 2.5开始,您可以使用transform_key方法: https : //docs.ruby-lang.org/en/trunk/Hash.html#method-i-transform_keys

所以在你的情况下会是:

 h = { "id" => "123", "name" => "test" } h.transform_keys!(&:to_sym) #=> {:id=>"123", :name=>"test"} 

注意:Ruby on Rails中也有相同的方法。