Melhor maneira de converter strings em símbolos em hash

Qual é a maneira (mais rápida / mais limpa / simples) de converter todas as chaves em um hash de strings para símbolos em Ruby?

Isso seria útil ao analisar o YAML.

my_hash = YAML.load_file('yml') 

Eu gostaria de poder usar:

 my_hash[:key] 

Ao invés de:

 my_hash['key'] 

Se você quer um one-liner,

 my_hash = my_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo} 

irá copiar o hash para um novo com as teclas simbolizadas.

Aqui está um método melhor, se você estiver usando Rails:

params. symbolize_keys

O fim.

Se você não é, apenas copie seu código (também está no link):

 myhash.keys.each do |key| myhash[(key.to_sym rescue key) || key] = myhash.delete(key) end 

Para o caso específico de YAML em Ruby, se as chaves começarem com ‘ : ‘, elas serão automaticamente internadas como símbolos.

 exigir 'yaml'
 requer 'pp'
 yaml_str = "
 conexões:
   - host: host1.example.com
     porta: 10000
   - host: host2.example.com
     porta: 20000
 "
 yaml_sym = "
 conexões:
   -: host: host1.example.com
     : porta: 10000
   -: host: host2.example.com
     : porta: 20000
 "
 pp yaml_str = YAML.load (yaml_str)
 puts yaml_str.keys.first.class
 pp yaml_sym = YAML.load (yaml_sym)
 puts yaml_sym.keys.first.class

Saída:

 # /opt/ruby-1.8.6-p287/bin/ruby ~ / test.rb
 {"conexões" =>
   [{"port" => 10000, "host" => "host1.example.com"},
    {"porta" => 20000, "host" => "host2.example.com"}]}
 Corda
 {: connections =>
   [{: port => 10000,: host => "host1.example.com"},
    {: port => 20000,: host => "host2.example.com"}]}
 Símbolo

Ainda mais conciso:

 Hash[my_hash.map{|(k,v)| [k.to_sym,v]}] 

Se você estiver usando o Rails, é muito mais simples – você pode usar um HashWithIndifferentAccess e acessar as chaves tanto como String quanto como Símbolos:

 my_hash.with_indifferent_access 

Veja também:

http://api.rubyonrails.org/classs/ActiveSupport/HashWithIndifferentAccess.html


Ou você pode usar a incrível “Facets of Ruby” Gem, que contém muitas extensões para as classs Ruby Core e Standard Library.

  require 'facets' > {'some' => 'thing', 'foo' => 'bar'}.symbolize_keys => {:some=>"thing", :foo=>"bar} 

ver também: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash

http://api.rubyonrails.org/classs/Hash.html#method-i-symbolize_keys

 hash = { 'name' => 'Rob', 'age' => '28' } hash.symbolize_keys # => { name: "Rob", age: "28" } 

Aqui está uma maneira de simbolizar profundamente um object

 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 

Eu realmente gosto da gema Mash .

você pode fazer mash['key'] , ou mash[:key] , ou mash.key

Desde o Ruby 2.5.0 você pode usar Hash#transform_keys ou Hash#transform_keys! .

 {'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2} 

Uma modificação na resposta do @igorsales

 class Object def deep_symbolize_keys return self.inject({}){|memo,(k,v)| memo[k.to_sym] = v.deep_symbolize_keys; memo} if self.is_a? Hash return self.inject([]){|memo,v | memo << v.deep_symbolize_keys; memo} if self.is_a? Array return self end end 

params.symbolize_keys também funcionará. Esse método transforma chaves hash em símbolos e retorna um novo hash.

Tantas respostas aqui, mas a function de rails de um método é hash.symbolize_keys

Este é o meu forro para hashes nesteds

 def symbolize_keys(hash) hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v } end 
 {'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys! 

Converte para:

 {:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}} 

Se você está usando o json, e quer usá-lo como um hash, no Ruby você pode fazer isso:

 json_obj = JSON.parse(json_str, symbolize_names: true) 

symbolize_names : Se definido como true, retorna símbolos para os nomes (chaves) em um object JSON. Caso contrário, as strings são retornadas. Strings são o padrão.

Doc: Json # parse symbolize_names

Você poderia ser preguiçoso e envolvê-lo em um lambda :

 my_hash = YAML.load_file('yml') my_lamb = lambda { |key| my_hash[key.to_s] } my_lamb[:a] == my_hash['a'] #=> true 

Mas isso só funcionaria para ler do hash – não escrever.

Para fazer isso, você poderia usar o Hash#merge

 my_hash = Hash.new { |h,k| h[k] = h[k.to_s] }.merge(YAML.load_file('yml')) 

O bloco de boot converterá as chaves uma vez sob demanda, mas se você atualizar o valor da versão de string da chave depois de acessar a versão do símbolo, a versão do símbolo não será atualizada.

 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} 

Você também pode fazer com que o bloco init não atualize o hash, o que protegeria você desse tipo de erro, mas você ainda estaria vulnerável ao contrário – a atualização da versão do símbolo não atualizaria a versão da string:

 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} 

Então, a coisa a ser cuidadosa com estes é alternar entre os dois formulários principais. Fique com um.

Caso o motivo pelo qual você precise fazer isso seja porque seus dados vieram originalmente do JSON, você pode ignorar qualquer uma dessas análises passando apenas a opção :symbolize_names ao processar o JSON.

Não requer Rails e funciona com Ruby> 1.9

 JSON.parse(my_json, :symbolize_names => true) 

um fwiw de uma linha mais curta:

 my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) } 

Algo como o seguinte trabalho?

 new_hash = Hash.new my_hash.each { |k, v| new_hash[k.to_sym] = v } 

Ele copia o hash, mas você não se importará com isso a maior parte do tempo. Há provavelmente uma maneira de fazer isso sem copiar todos os dados.

Que tal agora:

 my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml')) # my_hash['key'] => "val" # my_hash[:key] => "val" 

Isto é para pessoas que usam mruby e não possuem nenhum método symbolize_keys definido:

 class Hash def symbolize_keys! self.keys.each do |k| if self[k].is_a? Hash self[k].symbolize_keys! end if k.is_a? String raise RuntimeError, "Symbolizing key '#{k}' means overwrite some data (key :#{k} exists)" if self[k.to_sym] self[k.to_sym] = self[k] self.delete(k) end end return self end end 

O método:

  • simboliza apenas as chaves que são String
  • se simbolizar uma string significa perder algumas informações (sobrescrever parte do hash) gerar um RuntimeError
  • simbolizar também hashes contidos recursivamente
  • retornar o hash simbolizado
  • funciona no lugar!

O array que queremos mudar.

strings = [“HTML”, “CSS”, “JavaScript”, “Python”, “Ruby”]

Crie uma nova variável como uma matriz vazia para que possamos “empurrar” os símbolos.

símbolos = []

Aqui é onde nós definimos um método com um bloco.

strings.each {| x | symbols.push (x.intern)}

Fim do código.

Portanto, esta é provavelmente a maneira mais simples de converter strings em símbolos em sua matriz em Ruby. Faça uma matriz de strings, em seguida, faça uma nova variável e defina a variável para uma matriz vazia. Em seguida, selecione cada elemento na primeira matriz criada com o método “.each”. Em seguida, use um código de bloco para “.push” todos os elementos em sua nova matriz e use “.intern ou .to_sym” para converter todos os elementos em símbolos.

Os símbolos são mais rápidos porque salvam mais memory em seu código e você só pode usá-los uma vez. Os símbolos são mais comumente usados ​​para chaves em hash, o que é ótimo. Eu não sou o melhor programador de rubys, mas esta forma de código me ajudou muito.Se alguém sabe uma maneira melhor, por favor, compartilhe e você pode usar esse método para hash também!

Se você gostaria de solução de baunilha ruby e como eu não tenho access ao ActiveSupport aqui é profundo simbolizar solução (muito semelhante aos anteriores)

  def deep_convert(element) return element.collect { |e| deep_convert(e) } if element.is_a?(Array) return element.inject({}) { |sh,(k,v)| sh[k.to_sym] = deep_convert(v); sh } if element.is_a?(Hash) element end 
 ruby-1.9.2-p180 :001 > h = {'aaa' => 1, 'bbb' => 2} => {"aaa"=>1, "bbb"=>2} ruby-1.9.2-p180 :002 > Hash[h.map{|a| [a.first.to_sym, a.last]}] => {:aaa=>1, :bbb=>2} 

Este não é exatamente um one-liner, mas transforma todas as chaves de string em símbolos, também os nesteds:

 def recursive_symbolize_keys(my_hash) case my_hash when Hash Hash[ my_hash.map do |key, value| [ key.respond_to?(:to_sym) ? key.to_sym : key, recursive_symbolize_keys(value) ] end ] when Enumerable my_hash.map { |value| recursive_symbolize_keys(value) } else my_hash end end 

Eu gosto deste one-liner, quando não estou usando o Rails, porque então eu não preciso fazer um segundo hash e manter dois conjuntos de dados enquanto o estou processando:

 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 retorna o valor da chave apagada

O Hash # deep_rekey dos facets também é uma boa opção, especialmente:

  • se você encontrar uso para outros açucares de facetas em seu projeto,
  • Se você preferir a legibilidade do código sobre os one-liners criptocharts.

Amostra:

 require 'facets/hash/deep_rekey' my_hash = YAML.load_file('yml').deep_rekey 

Em Ruby, acho que esta é a maneira mais simples e fácil de entender para transformar chaves de string em hashes em símbolos:

 my_hash.keys.each { |key| my_hash[key.to_sym] = my_hash.delete(key)} 

Para cada chave no hash, chamamos delete nela, que a remove do hash (também delete retorna o valor associado à chave que foi excluída) e imediatamente definimos esse valor como igual à chave simbolizada.

symbolize_keys recursivamente para qualquer hash:

 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