如何转换 Ruby 散列,使其所有键都是符号?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/8379596/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-06 04:33:32  来源:igfitidea点击:

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

rubyhash

提问by ed1t

I have a Ruby hash which looks like:

我有一个 Ruby 哈希,它看起来像:

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

I would like to convert it to:

我想将其转换为:

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

回答by Victor Moroz

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):

@mu 太短了:没有看到“递归”这个词,但如果你坚持(连同防止不存在的保护to_sym,只是想提醒一下,在 Ruby 1.8 中1.to_sym == nil,所以玩一些关键类型可能会产生误导):

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

回答by mu is too short

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

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

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

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

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

symbolize_keys!执行相同的操作,但就地操作。所以,如果你在 Rails 中,你可以:

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? Hashto match your specific circumstances; using respond_to? :keysmight make more sense. And if you want to allow for keys that don't understand to_sym, then:

您可能想根据kind_of? Hash自己的具体情况使用 ;使用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

Note that h[ks] = h.delete kdoesn't change the content of the Hash when k == ksbut 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.

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

The second symbolize_keys_deep!turns this:

第二个symbolize_keys_deep!变成了这个:

{ '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.

symbolize_keys_deep!如果你真的想的话,你可以将任何一个版本的猴子修补到 Hash 中,但我通常远离猴子修补,除非我有很好的理由这样做。

回答by MRifat

If you are using Rails >= 4 you can use:

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

hash.deep_symbolize_keys
hash.deep_symbolize_keys!

or

或者

hash.deep_stringify_keys
hash.deep_stringify_keys!

see http://apidock.com/rails/v4.2.1/Hash/deep_symbolize_keys

http://apidock.com/rails/v4.2.1/Hash/deep_symbolize_keys

回答by Alexander

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

以防万一您正在解析 JSON,您可以从JSON 文档中添加选项以在解析时对键进行符号化:

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

回答by pje

Victor Morozprovided a lovely answer for the simple recursive case, but it won't process hashes that are nested within nested arrays:

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

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

If you need to support hashes within arrays within hashes, you'll want something more like this:

如果您需要在散列中的数组内支持散列,您将需要更像这样的东西:

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

回答by John Feminella

Try this:

尝试这个:

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

This iterates over the keys, and for each one, it deletes the stringified key and assigns its value to the symbolized key.

这将迭代键,对于每个键,它删除字符串化键并将其值分配给符号化键。

回答by bonkydog

If you're using Rails (or just Active Support):

如果您使用 Rails(或仅使用 Active Support):

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

回答by Paulo Fidalgo

Starting with Ruby 2.5 you can use the transform_keymethod.

从 Ruby 2.5 开始,您可以使用该transform_key方法。

So in your case it would be:

所以在你的情况下,它将是:

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

Note: the same methods are also available on Ruby on Rails.

注意:同样的方法也可以在 Ruby on Rails 上使用。

回答by Chakaitos

Here's a Ruby one-liner that is faster than the chosen answer:

这是一个比所选答案更快的 Ruby 单线:

hash = {"apple" => "banana", "coconut" => "domino"}
#=> {"apple"=>"banana", "coconut"=>"domino"}

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

Benchmark results:

基准测试结果:

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)

回答by David Fabreguette

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

您还可以扩展核心 Hash 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

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 :

如果您想确保包装到父哈希中的数组中的任何哈希的键都被符号化,您还需要扩展数组类,使用该代码创建一个“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

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

这允许调用“symbolize_keys_deep!” 在任何这样的哈希变量上:

myhash.symbolize_keys_deep!