example create ruby-on-rails ruby ruby-on-rails-3 recursion hash

ruby on rails - create - Iterato sobre un nivel de hashes profundamente anidado en Ruby



ruby create hash (7)

Así que tengo un hash, y para cada nivel del hash, quiero almacenar su clave y valor. El problema es que un valor puede ser otra matriz de hash. Además, ese hash puede contener pares de valores clave donde el valor es otra vez matriz de hash, etc., etc. Además, no sabré cuán profundamente anidado estará cada hash. Para dar un ejemplo:

{ :key1 => ''value1'', :key2 => ''value2'', :key3 => { :key4 => ''value4'', :key5 => ''value5'' }, :key6 => { :key7 => ''value7'', :key8 => { :key9 => ''value9'' } } }

..Y así. Lo que quiero hacer es guardar cada clave, par de valores y la identificación de su padre. Me imagino que esto probablemente se hará recursivamente, no estoy seguro de cómo porque no estoy familiarizado con las funciones recursivas. Sé cómo iterar a través de los datos normalmente:

myHash.each {|key, value| ...Do something with the key and value ... }

Y entonces supongo que la llamada recursiva será algo como esto:

def save_pair (myHash) myHash.each {|key, value| if(value.class != Hash) ? Pair.create(key, value) : save_pair(value) } end

Esto no se ha probado, y todavía no estoy seguro de cómo incorporar el guardado de los ID principales independientemente.


¿Has probado algo así?

trios = [] def save_trio(hash, parent = nil) hash.each do |key, value| value.kind_of?(Hash) ? save_trio(value, key) : trios << {:key => key, :value => value, :parent => parent} end end save_trio(myHash)


Aquí está la versión recursiva (lectura mejorada ) de Hash::each ( Hash::each_pair ) con bloque y soporte de enumerador :

module HashRecursive refine Hash do def each(recursive=false, &block) if recursive Enumerator.new do |yielder| self.map do |key, value| value.each(recursive=true).map{ |key_next, value_next| yielder << [[key, key_next].flatten, value_next] } if value.is_a?(Hash) yielder << [[key], value] end end.entries.each(&block) else super(&block) end end alias_method(:each_pair, :each) end end using HashRecursive

Aquí hay ejemplos de uso de Hash::each con y sin indicador recursive :

hash = { :a => { :b => { :c => 1, :d => [2, 3, 4] }, :e => 5 }, :f => 6 } p hash.each, hash.each {}, hash.each.size # #<Enumerator: {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6}:each> # {:a=>{:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}, :f=>6} # 2 p hash.each(true), hash.each(true) {}, hash.each(true).size # #<Enumerator: [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]]:each> # [[[:a, :b, :c], 1], [[:a, :b, :d], [2, 3, 4]], [[:a, :b], {:c=>1, :d=>[2, 3, 4]}], [[:a, :e], 5], [[:a], {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5}], [[:f], 6]] # 6 hash.each do |key, value| puts "#{key} => #{value}" end # a => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5} # f => 6 hash.each(true) do |key, value| puts "#{key} => #{value}" end # [:a, :b, :c] => 1 # [:a, :b, :d] => [2, 3, 4] # [:a, :b] => {:c=>1, :d=>[2, 3, 4]} # [:a, :e] => 5 # [:a] => {:b=>{:c=>1, :d=>[2, 3, 4]}, :e=>5} # [:f] => 6 hash.each_pair(recursive=true) do |key, value| puts "#{key} => #{value}" unless value.is_a?(Hash) end # [:a, :b, :c] => 1 # [:a, :b, :d] => [2, 3, 4] # [:a, :e] => 5 # [:f] => 6

Aquí hay un ejemplo de la pregunta en sí:

hash = { :key1 => ''value1'', :key2 => ''value2'', :key3 => { :key4 => ''value4'', :key5 => ''value5'' }, :key6 => { :key7 => ''value7'', :key8 => { :key9 => ''value9'' } } } hash.each_pair(recursive=true) do |key, value| puts "#{key} => #{value}" unless value.is_a?(Hash) end # [:key1] => value1 # [:key2] => value2 # [:key3, :key4] => value4 # [:key3, :key5] => value5 # [:key6, :key7] => value7 # [:key6, :key8, :key9] => value9

También eche un vistazo a mi versión recursiva de Hash::merge ( Hash::merge! ) here .


Esto debería ser bueno para JSON. Mejoras menores al código de Mark donde convierte todo a mayúsculas en un hash dado:

def capitalize_hash(myHash) myHash.each {|key, value| puts "isHash: #{value.is_a?(Hash)}: " + value.to_s value.is_a?(Hash) ? capitalize_hash(value) : ( value.is_a?(Array) ? (myHash[key] = capitalize_array(value)) : (myHash[key] = value.try(:upcase))) } end def capitalize_array(myArray) myArray.each {|value| puts "isHash: #{value.is_a?(Hash)}: " + value.to_s value.is_a?(Array) ? capitalize_array(value) : ( value.is_a?(Hash) ? capitalize_hash(value) : value.try(:upcase)) } end



Sé que esta es una respuesta tardía, pero acabo de implementar una solución no recursiva a su problema y pensé que valía la pena compartirla.

class Hash def deep_traverse(&block) stack = self.map{ |k,v| [ [k], v ] } while not stack.empty? key, value = stack.pop yield(key, value) if value.is_a? Hash value.each{ |k,v| stack.push [ key.dup << k, v ] } end end end end

Luego, volviendo a su problema original, puede hacer:

h = { :key1 => ''value1'', :key2 => ''value2'', :key3 => { :key4 => ''value4'', :key5 => ''value5'' }, :key6 => { :key7 => ''value7'', :key8 => { :key9 => ''value9'' } } } h.deep_traverse{ |path,value| p [ path, value ] } # => [[:key6], {:key7=>"value7", :key8=>{:key9=>"value9"}}] # [[:key6, :key8], {:key9=>"value9"}] # [[:key6, :key8, :key9], "value9"] # [[:key6, :key7], "value7"] # [[:key3], {:key4=>"value4", :key5=>"value5"}] # [[:key3, :key5], "value5"] # [[:key3, :key4], "value4"] # [[:key2], "value2"] # [[:key1], "value1"]

También hay una versión esencial .


Si entiendo el objetivo, entonces debes poder pasar al padre a tu método de guardar. Para el nivel superior, será nulo. A continuación, se muestra la idea en la que se usa puts como marcador de posición para "guardar".

def save_pair(parent, myHash) myHash.each {|key, value| value.is_a?(Hash) ? save_pair(key, value) : puts("parent=#{parent.nil? ? ''none'':parent}, (#{key}, #{value})") } end

Aquí hay un ejemplo de llamada:

hash = Hash.new hash["key1"] = "value1" hash["key2"] = "value2" hash["key3"] = Hash.new hash["key3"]["key4"] = "value4" hash["key3"]["key5"] = "value5" hash["key6"] = Hash.new hash["key6"]["key7"] = "value7" hash["key6"]["key8"] = Hash.new hash["key6"]["key8"]["key9"] = "value9" save_pair(nil, hash)


class Hash def each_with_parent(parent=nil, &blk) each do |k, v| Hash === v ? v.each_with_parent(k, &blk) : blk.call([parent, k, v]) end end end h = { :a => 1, :b => { :c => 3, :d => 4, :e => { :f => 5 } } } h.each_with_parent { |i| p i } # [nil, :a, 1] # [:b, :c, 3] # [:b, :d, 4] # [:e, :f, 5]