迭代Ruby中深度嵌套的哈希级别

所以我有一个哈希,对于每个哈希级别,我想存储它的键和值。 问题是,值可以是另一个哈希数组。 此外,该哈希可以包含键值对,其中值又是另一个哈希数组等等。另外,我不知道每个哈希的嵌套程度有多深。 举个例子:

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

..等等。 我想要做的是保存每个键,值对和其父级的ID。 我认为这可能是递归完成的,我只是不确定如何因为我不熟悉递归函数。 我知道如何正常迭代数据:

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

所以我猜测递归调用将是这样的:

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

这是未经测试的,我仍然不确定如何合并保存父ID。

如果我理解了目标,那么您应该能够将父级传递给save方法。 对于顶级,它将是零。 以下显示了puts用作“保存”的占位符的想法。

 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 

以下是对它的示例调用:

 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 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 

然后,回到原来的问题,你可以这样做:

 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"] 

还有一个要点版本 。

 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| pi } # [nil, :a, 1] # [:b, :c, 3] # [:b, :d, 4] # [:e, :f, 5] 

这是具有枚举器支持的Hash::eachHash::each_pair )的递归 (读改进 )版本:

 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 

以下是Hash::each使用示例Hash::each都有和没有recursive标志:

 hash = { :a => { :b => { :c => 1, :d => [2, 3, 4] }, :e => 5 }, :f => 6 } p hash.each, hash.each {}, hash.each.size # #{: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 # #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 

以下是问题本身的示例:

 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 

另外在这里看看我的Hash::mergeHash::merge! )的递归版本。

你尝试过类似的东西吗?

 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) 

这应该适用于JSON。 Mark代码的一些小改进,它将给定哈希中的所有内容转换为大写:

 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 

我建议使用hashie gem的#deep_locate https://www.rubydoc.info/github/intridea/hashie/Hashie/Extensions/DeepLo​​cate#deep_locate-instance_method

有点hacky总是返回虚假而不是寻求

 hash.extend(Hashie::Extensions::DeepLocate) hash.deep_locate -> (key, value, object) do # what you want to do here! # key: hash key # value: hash value # object: hash_object false # prevent to stop seeking end