在 Ruby 中迭代深度嵌套的哈希级别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/8748475/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Iterate over a deeply nested level of hashes in Ruby
提问by varatis
So I have a hash, and for each level of the hash, I want to store its key and value. The problem is, a value can be another hash array. Furthermore, that hash can contain key value pairs where the value is again another hash array, etc, etc. Also, I won't know how deeply nested each hash will be. To give an example:
所以我有一个散列,对于散列的每一级,我想存储它的键和值。问题是,一个值可以是另一个哈希数组。此外,该散列可以包含键值对,其中该值又是另一个散列数组,等等。此外,我不知道每个散列的嵌套深度。举个例子:
{
:key1 => 'value1',
:key2 => 'value2',
:key3 => {
:key4 => 'value4',
:key5 => 'value5'
},
:key6 => {
:key7 => 'value7',
:key8 => {
:key9 => 'value9'
}
}
}
..And so on. What I want to do is save each key, value pair and the id of its parent.I figure this will probably be done recursively, I'm just unsure how because I'm unfamiliar with recursive functions. I know how to iterate through the data normally:
..等等。我想要做的是保存每个键、值对及其父级的 id。我认为这可能会递归完成,我只是不确定如何,因为我不熟悉递归函数。我知道如何正常遍历数据:
myHash.each {|key, value|
...Do something with the key and value ...
}
And so I'm guessing the recursive call will be something like this:
所以我猜递归调用将是这样的:
def save_pair (myHash)
myHash.each {|key, value|
if(value.class != Hash) ? Pair.create(key, value) : save_pair(value)
}
end
This is untested, and I'm still unsure how to incorporate saving the parent ids regardless.
这是未经测试的,我仍然不确定如何合并保存父 ID。
采纳答案by Mark Wilkins
If I understand the goal, then you should be able to pass in the parent to your save method. For the top level, it will be nil. The following shows the idea where putsis used as a place holder for the "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
Here is an example call to it:
这是对它的调用示例:
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)
回答by sebastian
I know this is a late reply, but I just implemented a non-recursive solution to your problem and thought it is worth sharing.
我知道这是一个迟到的回复,但我刚刚为您的问题实施了一个非递归解决方案,并认为值得分享。
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
Then, coming back to your original problem, you can do:
然后,回到你原来的问题,你可以这样做:
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"]
There is also a gist version.
还有一个要点版本。
回答by Marek P?íhoda
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]
回答by MOPO3OB
Here is recursive(read improved) version of Hash::each(Hash::each_pair) with blockand enumeratorsupport:
这是具有块和枚举器支持的( ) 的递归(读取改进)版本: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
Here are usage examplesof Hash::eachwith and without recursiveflag:
下面是使用实例的Hash::each有和无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
Here is example from the question itself:
以下是问题本身的示例:
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
Also take a look at my recursive version of Hash::merge(Hash::merge!) here.
还可以在此处查看我的Hash::merge( Hash::merge!)递归版本。
回答by Matsumoto Kazuya
I recomend to use #deep_locate of hashie gemhttps://www.rubydoc.info/github/intridea/hashie/Hashie/Extensions/DeepLocate#deep_locate-instance_method
我建议使用 #deep_locate of hashie gem https://www.rubydoc.info/github/intridea/hashie/Hashie/Extensions/DeepLocate#deep_locate-instance_method
little bit hacky always return false not to seeking
一点点hacky总是返回false而不是寻求
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
回答by Arnold Roa
If you want to recursively edit the hash, you could do something like this:
如果要递归编辑哈希,可以执行以下操作:
# Iterates over a Hash recursively
def each_recursive(parent, &block)
parent.each do |path, value|
if value.kind_? Hash
each_recursive parent, &block
elsif value.is_a? Array
# @TODo something different for Array?
else
yield(parent, path, container_or_field)
end
end
end
And you can do something like:
您可以执行以下操作:
hash = {...}
each_recursive(hash) do |parent, path, value|
parent[path] = value.uppercase
end
回答by GG.
This should do for JSON well. Minor enhancements to Mark's code where it converts everything to uppercase in a given hash:
这应该很好地适用于 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
回答by christianblais
Have you tried something like that?
你有没有尝试过这样的事情?
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)

