validar update rails create array ruby hashmap

rails - update hash ruby



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

¿Cuál es la forma (más rápida / limpia / directa) 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]

Más bien que:

my_hash[''key'']


La matriz que queremos cambiar.

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

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

símbolos = []

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

cuerdas. cada {| x | symbols.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. Haga una matriz de cadenas, luego cree una nueva variable y establezca la variable en una matriz vacía. Luego seleccione cada elemento en la primera matriz que creó con el método ".each". Luego use un código de bloque para ".push" 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 en su código y solo puede usarlos una vez. Los símbolos son más comúnmente utilizados para las claves en hash lo que es genial. No soy el mejor programador de Ruby, pero esta forma de código me ayudó mucho. Si alguien sabe una manera mejor, por favor, comparta y puede usar este método para el hash también.



¿Algo como el siguiente trabajo?

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.


Aún más terso:

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


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

params symbolize_keys

El fin.

Si no lo está, simplemente copie 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


Aquí hay una manera de simbolizar en profundidad 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


En caso de que la razón por la que necesite hacer esto sea porque sus datos provinieron originalmente de JSON, podría omitir cualquiera de estos análisis simplemente pasando la opción :symbolize_names al ingerir JSON.

No requiere rieles y funciona con Ruby> 1.9

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


En ruby, considero que esta es la forma más sencilla y fácil de entender para convertir claves de cadena en hashes a 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 ella, que lo elimina del hash (también delete devuelve el valor asociado con la clave que se eliminó) e inmediatamente establecemos este valor igual a la clave simbolizada.


Este es mi único forro 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


Esto es para las personas que usan mruby y no tienen definido ningún método symbolize_keys :

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 teclas que son String
  • Si simbolizar una cadena significa perder algunas informaciones (sobrescribir parte del hash) RuntimeError un RuntimeError
  • simbolizan también hashes contenidas recursivamente
  • devolver el hash simbolizado
  • trabaja en su lugar!

Esto no es exactamente de una sola línea, pero convierte todas las claves 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


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


Para el caso específico de YAML en Ruby, si las teclas 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


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 el hash, no 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 de inicio convertirá las claves una vez a petición, 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 puede hacer que el bloque de inicio no actualice el hash, lo que lo protegería de ese tipo de error, pero aún sería vulnerable a lo contrario: actualizar la versión del símbolo no actualizaría la versión de la 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}

Por lo tanto, hay que tener cuidado con estos cambios entre las dos formas clave. Quédate con uno.


Qué tal esto:

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


Realmente me gusta la gema Mash .

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


Si desea una solución de vainilla rubí y, como yo, no tengo acceso a ActiveSupport aquí encontrará una solución de simbolización profunda (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


Si está usando Rails, es mucho más simple: puede usar un HashWithIndifferentAccess y acceder a las teclas como Cadena y como Símbolos:

my_hash.with_indifferent_access

ver también:

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

O puede usar la impresionante "Facetas de Ruby" Gem, que contiene muchas extensiones para las clases de Ruby Core y de la Biblioteca estándar.

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

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


Si está utilizando json y desea utilizarlo como un hash, en Ruby Core puede hacerlo:

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

simbolizar nombres: si se establece en verdadero, 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


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 teclas simbolizadas.


Similar a soluciones anteriores pero escrito un poco diferente.

  • Esto permite un hash que está anidado y / o tiene matrices.
  • Obtener la conversión de claves a una cadena como un bono.
  • El código no muta el hash que se ha pasado.

    module HashUtils def symbolize_keys(hash) transformer_function = ->(key) { key.to_sym } transform_keys(hash, transformer_function) end def stringify_keys(hash) transformer_function = ->(key) { key.to_s } transform_keys(hash, transformer_function) end def transform_keys(obj, transformer_function) case obj when Array obj.map{|value| transform_keys(value, transformer_function)} when Hash obj.each_with_object({}) do |(key, value), hash| hash[transformer_function.call(key)] = transform_keys(value, transformer_function) end else obj end end end


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


Tantas respuestas aquí, pero la función de un solo método es hash.symbolize_keys


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


un fwiw más corto de una sola línea:

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


symbolize_keys

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


Facets ''Hash # deep_rekey también es una buena opción, especialmente:

  • Si encuentra uso para otras azúcares de facetas en su proyecto,
  • Si prefieres la legibilidad del código en vez de las crípticas de una sola línea.

Muestra:

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


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


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}


{''g''=> ''a'', 2 => {''v'' => ''b'', ''x'' => { ''z'' => ''c''}}}.deep_symbolize_keys!

Convierte a:

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