update rails create array ruby hash key

rails - ruby each hash key value



¿Cómo renombrar elegantemente todas las teclas en un hash en Ruby? (11)

Esta pregunta ya tiene una respuesta aquí:

Tengo un hash Ruby:

ages = { "Bruce" => 32, "Clark" => 28 }

Suponiendo que tengo otro hash de nombres de reemplazo, ¿hay alguna forma elegante de cambiar el nombre de todas las claves para que termine con:

ages = { "Bruce Wayne" => 32, "Clark Kent" => 28 }


Es posible que desee utilizar Object#tap para evitar la necesidad de devolver las ages después de que se hayan modificado las claves:

ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.tap {|h| h.keys.each {|k| (h[mappings[k]] = h.delete(k)) if mappings.key?(k)}} #=> {"Bruce Wayne"=>32, "Clark Kent"=>28}


La gema Facets proporciona un método rekey que hace exactamente lo que te falta.

Siempre que esté de acuerdo con la dependencia de la gema Facets , puede pasar un hash de asignaciones para rekey a rekey y devolverá un nuevo hash con las nuevas claves:

require ''facets/hash/rekey'' ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.rekey(mappings) => {"Bruce Wayne"=>32, "Clark Kent"=>28}

Si desea modificar hash de edades en su lugar, puede usar la rekey! versión:

ages.rekey!(mappings) ages => {"Bruce Wayne"=>32, "Clark Kent"=>28}


Me gustó la respuesta de Jörg W Mittag, pero se puede mejorar.

Si desea cambiar el nombre de las teclas de su Hash actual y no crear un nuevo Hash con las claves renombradas, el siguiente fragmento de código hace exactamente eso:

ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.keys.each { |k| ages[ mappings[k] ] = ages.delete(k) if mappings[k] } ages

También existe la ventaja de cambiar el nombre de las claves necesarias.

Consideraciones de rendimiento:

Basado en la respuesta de Tin Man , mi respuesta es aproximadamente un 20% más rápida que la respuesta de Jörg W Mittag para un hash con solo dos teclas. Puede obtener un rendimiento aún mayor para Hashes con muchas claves, especialmente si hay solo unas pocas claves para renombrar.


Realicé un parche en el mono de la clase para manejar Hashes y Arrays anidados:

# Netsted Hash: # # str_hash = { # "a" => "a val", # "b" => "b val", # "c" => { # "c1" => "c1 val", # "c2" => "c2 val" # }, # "d" => "d val", # } # # mappings = { # "a" => "apple", # "b" => "boss", # "c" => "cat", # "c1" => "cat 1" # } # => {"apple"=>"a val", "boss"=>"b val", "cat"=>{"cat 1"=>"c1 val", "c2"=>"c2 val"}, "d"=>"d val"} # class Hash def rename_keys(mapping) result = {} self.map do |k,v| mapped_key = mapping[k] ? mapping[k] : k result[mapped_key] = v.kind_of?(Hash) ? v.rename_keys(mapping) : v result[mapped_key] = v.collect{ |obj| obj.rename_keys(mapping) if obj.kind_of?(Hash)} if v.kind_of?(Array) end result end end


Si el Hash de mapeo será más pequeño que el Hash de datos, entonces itere en mapeos. Esto es útil para cambiar el nombre de algunos campos en un Hash grande:

class Hash def rekey(h) dup.rekey! h end def rekey!(h) h.each { |k, newk| store(newk, delete(k)) if has_key? k } self end end ages = { "Bruce" => 32, "Clark" => 28, "John" => 36 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} p ages.rekey! mappings


Solo para ver qué era más rápido:

require ''fruity'' AGES = { "Bruce" => 32, "Clark" => 28 } MAPPINGS = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} def jörg_w_mittag_test(ages, mappings) Hash[ages.map {|k, v| [mappings[k], v] }] end require ''facets/hash/rekey'' def tyler_rick_test(ages, mappings) ages.rekey(mappings) end def barbolo_test(ages, mappings) ages.keys.each { |k| ages[ mappings[k] ] = ages.delete(k) if mappings[k] } ages end class Hash def tfr_rekey(h) dup.tfr_rekey! h end def tfr_rekey!(h) h.each { |k, newk| store(newk, delete(k)) if has_key? k } self end end def tfr_test(ages, mappings) ages.tfr_rekey mappings end class Hash def rename_keys(mapping) result = {} self.map do |k,v| mapped_key = mapping[k] ? mapping[k] : k result[mapped_key] = v.kind_of?(Hash) ? v.rename_keys(mapping) : v result[mapped_key] = v.collect{ |obj| obj.rename_keys(mapping) if obj.kind_of?(Hash)} if v.kind_of?(Array) end result end end def greg_test(ages, mappings) ages.rename_keys(mappings) end compare do jörg_w_mittag { jörg_w_mittag_test(AGES.dup, MAPPINGS.dup) } tyler_rick { tyler_rick_test(AGES.dup, MAPPINGS.dup) } barbolo { barbolo_test(AGES.dup, MAPPINGS.dup) } greg { greg_test(AGES.dup, MAPPINGS.dup) } end

Qué salidas:

Running each test 1024 times. Test will take about 1 second. barbolo is faster than jörg_w_mittag by 19.999999999999996% ± 10.0% jörg_w_mittag is faster than greg by 10.000000000000009% ± 10.0% greg is faster than tyler_rick by 30.000000000000004% ± 10.0%

Precaución: la solución de barra utiliza if mappings[k] , lo que hará que el hash resultante sea incorrecto si las mappings[k] resultan en un valor nulo.


También está el método each_with_object en Ruby:

ages = { "Bruce" => 32, "Clark" => 28 } mappings = { "Bruce" => "Bruce Wayne", "Clark" => "Clark Kent" } ages.each_with_object({}) { |(k, v), memo| memo[mappings[k]] = v }


Usé esto para permitir que los nombres "amigos" en una tabla de pepino se analizaran en atributos de clase de modo que Factory Girl pudiera crear una instancia:

Given(/^an organization exists with the following attributes:$/) do |table| # Build a mapping from the "friendly" text in the test to the lower_case actual name in the class map_to_keys = Hash.new table.transpose.hashes.first.keys.each { |x| map_to_keys[x] = x.downcase.gsub('' '', ''_'') } table.transpose.hashes.each do |obj| obj.keys.each { |k| obj[map_to_keys[k]] = obj.delete(k) if map_to_keys[k] } create(:organization, Rack::Utils.parse_nested_query(obj.to_query)) end end

Por lo que vale, la tabla de pepino se ve así:

Background: And an organization exists with the following attributes: | Name | Example Org | | Subdomain | xfdc | | Phone Number | 123-123-1234 | | Address | 123 E Walnut St, Anytown, PA 18999 | | Billing Contact | Alexander Hamilton | | Billing Address | 123 E Walnut St, Anytown, PA 18999 |

Y map_to_keys ve así:

{ "Name" => "name", "Subdomain" => "subdomain", "Phone Number" => "phone_number", "Address" => "address", "Billing Contact" => "billing_contact", "Billing Address" => "billing_address" }


>> x={ :a => ''qwe'', :b => ''asd''} => {:a=>"qwe", :b=>"asd"} >> rename={:a=>:qwe} => {:a=>:qwe} >> rename.each{|old,new| x[new] = x.delete old} => {:a=>:qwe} >> x => {:b=>"asd", :qwe=>"qwe"}

Esto pasará solo a través de renombrar hash.


ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages.map {|k, v| [mappings[k], v] }.to_h


ages = { "Bruce" => 32, "Clark" => 28 } mappings = {"Bruce" => "Bruce Wayne", "Clark" => "Clark Kent"} ages = mappings.inject({}) {|memo, mapping| memo[mapping[1]] = ages[mapping[0]]; memo} puts ages.inspect