La mejor manera de convertir cadenas a símbolos en hash


¿Cuál es la forma (más rápida/limpia/sencilla) de convertir todas las claves en un hash de cadenas a símbolos en Ruby?

Esto sería útil al analizar YAML.

my_hash = YAML.load_file('yml')

Me gustaría poder usar:

my_hash[:key] 

En lugar de:

my_hash['key']
 218
Author: Max, 2009-04-29

29 answers

Si quieres una sola línea,

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

Copiará el hash en uno nuevo con las claves simbolizadas.

 199
Author: Sarah Mei,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-13 05:02:12

Aquí hay un método mejor, si estás usando Rails:

Params. symbolize_keys

Fin.

Si no lo estás, simplemente roba su código (también está en el enlace):

myhash.keys.each do |key|
  myhash[(key.to_sym rescue key) || key] = myhash.delete(key)
end
 282
Author: Sai,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-16 15:41:57

Para el caso específico de YAML en Ruby, si las claves comienzan con ':', se internarán automáticamente como símbolos.

require 'yaml'
require 'pp'
yaml_str = "
connections:
  - host: host1.example.com
    port: 10000
  - host: host2.example.com
    port: 20000
"
yaml_sym = "
:connections:
  - :host: host1.example.com
    :port: 10000
  - :host: host2.example.com
    :port: 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

Salida:

#  /opt/ruby-1.8.6-p287/bin/ruby ~/test.rb
{"connections"=>
  [{"port"=>10000, "host"=>"host1.example.com"},
   {"port"=>20000, "host"=>"host2.example.com"}]}
String
{:connections=>
  [{:port=>10000, :host=>"host1.example.com"},
   {:port=>20000, :host=>"host2.example.com"}]}
Symbol
 111
Author: jrgm,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 03:52:25

Aún más conciso:

Hash[my_hash.map{|(k,v)| [k.to_sym,v]}]
 57
Author: Michael Barton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-07-09 17:45:13

Si estás usando Rails, es mucho más simple-puedes usar un Hashwithindiferentaccess y acceder a las claves tanto como Cadena como como Símbolos:

my_hash.with_indifferent_access 

Véase también:

Http://api.rubyonrails.org/classes/ActiveSupport/HashWithIndifferentAccess.html


O puede usar la impresionante gema "Facets of Ruby", que contiene muchas extensiones a las clases Ruby Core y Standard Library.

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

Véase también: http://rubyworks.github.io/rubyfaux/?doc=http://rubyworks.github.io/facets/docs/facets-2.9.3/core.json#api-class-Hash

 48
Author: Tilo,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-01-21 19:58:02

Http://api.rubyonrails.org/classes/Hash.html#method-i-symbolize_keys

hash = { 'name' => 'Rob', 'age' => '28' }
hash.symbolize_keys
# => { name: "Rob", age: "28" }
 30
Author: Ery,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-23 12:06:41

Aquí hay una forma de simbolizar profundamente un objeto

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
 25
Author: igorsales,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2012-08-03 01:30:28

Me gusta mucho la gema Mash.

Puedes hacer mash['key'], o mash[:key], o mash.key

 20
Author: ykaganovich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-05-21 00:27:46

Ya que Ruby 2.5.0 puede utilizar Hash#transform_keys o Hash#transform_keys!.

{'a' => 1, 'b' => 2}.transform_keys(&:to_sym) #=> {:a => 1, :b => 2}
 17
Author: Sagar Pandya,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-01-22 00:15:30

Una modificación a la respuesta de @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
 10
Author: Tony,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-04-04 15:32:18

params.symbolize_keys también funcionará. Este método convierte las claves hash en símbolos y devuelve un nuevo hash.

 10
Author: Jae Cho,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-10-11 03:51:45

Hay muchas respuestas aquí, pero la función de un método rails es hash.symbolize_keys

 7
Author: shakirthow,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-05-19 22:30:02

Este es mi único trazador de líneas para hashes anidados

def symbolize_keys(hash)
  hash.each_with_object({}) { |(k, v), h| h[k.to_sym] = v.is_a?(Hash) ? symbolize_keys(v) : v }
end
 7
Author: Nick Dobson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-08-19 17:05:51

Si estás usando json, y quieres usarlo como un hash, en core Ruby puedes hacerlo:

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

Symbolize_names : Si se establece en true, devuelve símbolos para los nombres (claves) en un objeto JSON. De lo contrario, se devuelven cadenas. Las cadenas son las predeterminadas.

Doc: Json # parse symbolize_names

 7
Author: Mark,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2018-05-04 11:42:19
{'g'=> 'a', 2 => {'v' => 'b', 'x' => { 'z' => 'c'}}}.deep_symbolize_keys!

Se convierte en:

{:g=>"a", 2=>{:v=>"b", :x=>{:z=>"c"}}}
 6
Author: JayJay,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-10-31 10:40:18

Podrías ser perezoso, y envolverlo en un lambda:

my_hash = YAML.load_file('yml')
my_lamb = lambda { |key| my_hash[key.to_s] }

my_lamb[:a] == my_hash['a'] #=> true

Pero esto solo funcionaría para leer desde el hash, no para escribir.

Para hacer eso, puedes usar Hash#merge

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

El bloque init convertirá las claves una vez bajo demanda, aunque si actualiza el valor de la versión de cadena de la clave después de acceder a la versión de símbolo, la versión de símbolo no se actualizará.

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}

También podría hacer que el bloque init no actualice el hash, lo que lo protegería de eso tipo de error, pero aún sería vulnerable a lo contrario: actualizar la versión de símbolo no actualizaría la versión de cadena:

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}

Así que lo que hay que tener cuidado con estos es cambiar entre las dos formas clave. Stick con uno.

 4
Author: rampion,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-28 23:37:34

En caso de que la razón que necesita hacer esto es porque sus datos provienen originalmente de JSON, puede omitir cualquiera de este análisis simplemente pasando la opción :symbolize_names al ingerir JSON.

No se requieren rails y funciona con Ruby >1.9

JSON.parse(my_json, :symbolize_names => true)
 4
Author: Adam Grant,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-07-16 22:55:07

Una línea más corta fwiw:

my_hash.inject({}){|h,(k,v)| h.merge({ k.to_sym => v}) }
 3
Author: sensadrome,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-01-16 15:34:00

¿Funcionaría algo como lo siguiente?

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

Copiará el hash, pero eso no te importará la mayor parte del tiempo. Probablemente hay una manera de hacerlo sin copiar todos los datos.

 2
Author: ChrisInEdmonton,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2009-04-28 22:49:35

¿qué tal esto:

my_hash = HashWithIndifferentAccess.new(YAML.load_file('yml'))

# my_hash['key'] => "val"
# my_hash[:key]  => "val"
 2
Author: Fredrik Boström,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2010-12-17 13:57:05

Esto es para personas que usan mruby y no tienen ningún symbolize_keys método 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

El método:

  • simboliza solo las llaves que son String
  • si simbolizar una cadena significa perder algunas informaciones (sobrescribir parte de hash) levantar un RuntimeError
  • simbolizan también hashes recursivamente contenidos
  • devuelve el hash simbolizado
  • funciona en su lugar!
 2
Author: Matteo Ragni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-09-08 06:16:43

El array que queremos cambiar.

Strings = ["HTML"," CSS"," JavaScript"," Python","Ruby"]

Hacer una nueva variable como una matriz vacía para que podamos ".empuje " los símbolos en.

Símbolos = [ ]

Aquí es donde definimos un método con un bloque.

Cadenas.cada {|x| símbolos.push (x.intern)}

Fin del código.

Así que esta es probablemente la forma más sencilla de convertir cadenas a símbolos en su(s) matriz (s) en Ruby. Hacer una matriz de cadenas y luego hacer una nueva variable y establecer la variable en una matriz vacía. Luego seleccione cada elemento en el primer array que creó con el ".cada método. Luego use un código de bloque para".presione "todos los elementos en su nueva matriz y use".intern o. to_sym " para convertir todos los elementos en símbolos.

Los símbolos son más rápidos porque ahorran más memoria dentro de tu código y solo puedes usarlos una vez. Los símbolos se usan más comúnmente para las claves en hash, lo cual es genial. No soy el mejor programador de ruby pero esto la forma de código me ayudó mucho.Si alguien conoce una mejor manera por favor compartir y se puede utilizar este método para hash también!

 1
Author: rubyguest123,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-05 08:20:01

Si quieres la solución vanilla ruby y como yo no tengo acceso a ActiveSupport aquí está deep symbolize solution (muy similar a las 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
 1
Author: Haris Krajina,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-11-27 09:26:19
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}
 0
Author: Vlad Khomich,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-07-24 09:41:00

Esto no es exactamente una línea, pero convierte todas las teclas de cadena en símbolos, también las anidadas:

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
 0
Author: ChristofferJoergensen,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-06-03 06:02:09

Me gusta este one-liner, cuando no estoy usando Rails, porque entonces no tengo que hacer un segundo hash y mantener dos conjuntos de datos mientras lo estoy procesando:

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 devuelve el valor de la clave eliminada

 0
Author: nvts8a,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-07-02 16:05:49

El hash de Facets#deep_rekey también es una buena opción, especialmente:

  • si encuentra uso para otro azúcar de facetas en su proyecto,
  • si prefiere la legibilidad del código en lugar de los one-liners crípticos.

Muestra:

require 'facets/hash/deep_rekey'
my_hash = YAML.load_file('yml').deep_rekey
 0
Author: Sergikon,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-11-02 07:12:01

En ruby encuentro que esta es la forma más simple y fácil de entender de convertir las teclas de cadena en hashes en símbolos:

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

Para cada clave en el hash llamamos delete en él que lo elimina del hash (también delete devuelve el valor asociado con la clave que se eliminó) e inmediatamente establecemos esto igual a la clave simbolizada.

 0
Author: Maikol88,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2017-08-02 04:02:51

Symbolize_keys recursivamente para cualquier 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
 -2
Author: Jakub Zak,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2016-02-16 14:11:53