我正在使用ClosureTree宝石在轨道上使用红宝石。我有一个嵌套的哈希,说
{
:a00=>{
:a10=>{
:a20=>{},
:a21=>{}
},
:a11=>{
:a22=>{
:a30=>{},
:a31=>{}
}
}
}
}
在此嵌套哈希中,我想找到任何给定键及其所有“超级键”的值。例如,对于:a30
,我想找到其值{}
以及嵌套在其中的哈希键:[:a00, :a11, :a22]
。
我发现了this article,它描述了一种满足我的准则的第一部分(查找键的值)的漂亮方法:
def deep_find(obj, key)
return obj[key] if obj.respond_to?(:key?) && obj.key?(key)
if obj.is_a? Enumerable
found = nil
obj.find { |*a| found = deep_find(a.last, key) }
found
end
end
但是,经过数小时的尝试,我仍然无法找到一种找到它们“超级钥匙”的方法。非常感谢您对此问题的帮助!
我会选择这样的东西:
def find_node_path(tree, search_key)
return unless tree.is_a?(Hash)
return [] if tree.key?(search_key)
tree.each do |key, node|
path = find_node_path(node, search_key)
return [key, *path] if path
end
nil
end
path = find_node_path(tree, :a30)
#=> [:a00, :a11, :a22]
# retrieve value
value = tree.dig(*path, :a30)
#=> {}
这将执行以下操作,如果当前nil
不是哈希,或者如果在当前tree
结构中找不到search_key
,它将返回tree
。
该方法遍历哈希的所有键/值对,并递归调用find_node_path
。如果返回nil
,则表示找不到search_key
,因此跳到循环中的下一个迭代。
如果返回值不是nil
,则意味着相对于search_key
,在path
中的node
处找到了tree
。在这种情况下,请在path
之前加上当前迭代的key
,然后将其返回。
注:尽管如果search_key
在结构中不是唯一的,则此解决方案也有效。它只会返回找到的第一个匹配项。由于此解决方案使用depth first,因此当[:a1, :a2]
设为tree = {a1: {a2: {a3: {}}}, b1: {a3: {}}}
时它将返回search_key = :a3
。
def extract(h,target_key)
return [target_key, h[target_key]] if h.key?(target_key)
h.each do |kk,v|
next unless v.is_a?(Hash)
arr = extract(v,target_key)
return [kk,*arr] unless arr.nil?
end
nil
end
h = {
:a00=>{
:a10=>{
:a20=>{1=>2},
:a21=>{3=>4}
},
:a11=>{
:a22=>{
:a30=>{5=>6},
:a31=>{7=>8}
}
}
}
}
[:a00, :a10, :a20, :a21, :a11, :a22, :a30, :a31, :a32].each do |k|
puts ":#{k} -> #{extract(h,k) || "nil"}"
end
target_key -> extract(h, target_key)
:a00 -> [:a00, {:a10=>{:a20=>{1=>2}, :a21=>{3=>4}},
:a11=>{:a22=>{:a30=>{5=>6}, :a31=>{7=>8}}}}]
:a10 -> [:a00, :a10, {:a20=>{1=>2}, :a21=>{3=>4}}]
:a20 -> [:a00, :a10, :a20, {1=>2}]
:a21 -> [:a00, :a10, :a21, {3=>4}]
:a11 -> [:a00, :a11, {:a22=>{:a30=>{5=>6}, :a31=>{7=>8}}}]
:a22 -> [:a00, :a11, :a22, {:a30=>{5=>6}, :a31=>{7=>8}}]
:a30 -> [:a00, :a11, :a22, :a30, {5=>6}]
:a31 -> [:a00, :a11, :a22, :a31, {7=>8}]
:a32 -> nil