如何从父节点搜索数组的嵌套散列和散列数组以及 return 多个匹配对象?
How to search nested hash of arrays and arrays of hash and return multiple matching objects from the parent node?
假设我有以下 ruby 嵌套哈希
hash_or_array = [{
"book1" => "buyer1",
"book2" => {
"book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
},
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
},
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"],
"book5" => {
"book5,1" => "buyer5"
}
}]
我想查找字符串 buyer35
。匹配后,它应该return以下
[
{
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
下面的解决方案(来自下面的另一个 SO 问题,link),return 是第一个匹配项,但我想弄清楚如何 return 多个匹配项
def recurse(obj, target)
case obj
when Array
obj.each do |e|
case e
when Array, Hash
rv = recurse(e, target)
return [rv] unless rv.nil?
when target
return e
end
end
when Hash
obj.each do |k,v|
case v
when Array, Hash
rv = recurse(v, target)
return {k=>rv} unless rv.nil?
when target
return {k=>v}
end
end
end
nil
end
这是原问答:
更新:正确的 return 格式应该是
[
{
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
下面的代码似乎可以为这种特定情况生成所需的输出:
hash_or_array.inject([]) do |result, x|
x.keep_if { |k, v| v.to_s =~ /buyer35/ }
result << x
end
这是一个递归搜索任何嵌套数组或散列中的目标值的函数。然后该函数用于 select 包含目标的条目的顶级条目 hash_or_array 并将它们添加到数组中。
require 'pp'
def find_value(obj, target, found = false)
found = true if obj == target
return found unless obj.is_a?(Array) || obj.is_a?(Hash)
case obj
when Hash
obj.each { |k, v| found = find_value(v, target, found) }
when Array
obj.each { |v| found = find_value(v, target, found) }
end
found
end
found_entries = hash_or_array.inject([]) {|entries, obj| entries << obj.select { |k, v| find_value({ k => v }, "buyer35") }}
pp found_entries
=>
[{"book3"=>
{"0"=>"buyer31",
"1"=>"buyer32",
"2"=>"buyer33",
"3"=>[{"4"=>"buyer34", "5"=>[10, 11], "6"=>[{"7"=>"buyer35"}]}]},
"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
这是针对您的“真实”问题的递归解决方案。因为它改变了原始对象,所以我使用了一个“技巧”来先制作一个深拷贝。 keep_entries_with
生成一个具有原始形状的对象作为您的输入,但由于您的输出形状不同,第二步只是将过滤后的结果转换为您想要的输出形状。
deep_copy = Marshal.load(Marshal.dump(hash_or_array))
def keep_entries_with(target, obj)
return unless obj.is_a?(Hash) || obj.is_a?(Array)
case obj
when Hash
keep_entries_with(target, obj.values)
obj.keep_if do |k, v|
v == target ||
v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
end
when Array
obj.each do |v|
keep_entries_with(target, v)
end
end
end
filtered = keep_entries_with("buyer35", deep_copy)
final_result = filtered.first.map { |k, v| { k => v } }
pp final_result
产生:
[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
{"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
假设我有以下 ruby 嵌套哈希
hash_or_array = [{
"book1" => "buyer1",
"book2" => {
"book21" => "buyer21", "book22" => ["buyer23", "buyer24", true]
},
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
},
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"],
"book5" => {
"book5,1" => "buyer5"
}
}]
我想查找字符串 buyer35
。匹配后,它应该return以下
[
{
"book3" => {
"0" => "buyer31", "1" => "buyer32", "2" => "buyer33",
"3" => [{
"4" => "buyer34",
"5" => [10, 11],
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
下面的解决方案(来自下面的另一个 SO 问题,link),return 是第一个匹配项,但我想弄清楚如何 return 多个匹配项
def recurse(obj, target)
case obj
when Array
obj.each do |e|
case e
when Array, Hash
rv = recurse(e, target)
return [rv] unless rv.nil?
when target
return e
end
end
when Hash
obj.each do |k,v|
case v
when Array, Hash
rv = recurse(v, target)
return {k=>rv} unless rv.nil?
when target
return {k=>v}
end
end
end
nil
end
这是原问答:
更新:正确的 return 格式应该是
[
{
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}
},
{
"book4" => ["buyer41", "buyer42", "buyer43", "buyer35"]
}
]
下面的代码似乎可以为这种特定情况生成所需的输出:
hash_or_array.inject([]) do |result, x|
x.keep_if { |k, v| v.to_s =~ /buyer35/ }
result << x
end
这是一个递归搜索任何嵌套数组或散列中的目标值的函数。然后该函数用于 select 包含目标的条目的顶级条目 hash_or_array 并将它们添加到数组中。
require 'pp'
def find_value(obj, target, found = false)
found = true if obj == target
return found unless obj.is_a?(Array) || obj.is_a?(Hash)
case obj
when Hash
obj.each { |k, v| found = find_value(v, target, found) }
when Array
obj.each { |v| found = find_value(v, target, found) }
end
found
end
found_entries = hash_or_array.inject([]) {|entries, obj| entries << obj.select { |k, v| find_value({ k => v }, "buyer35") }}
pp found_entries
=>
[{"book3"=>
{"0"=>"buyer31",
"1"=>"buyer32",
"2"=>"buyer33",
"3"=>[{"4"=>"buyer34", "5"=>[10, 11], "6"=>[{"7"=>"buyer35"}]}]},
"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]
这是针对您的“真实”问题的递归解决方案。因为它改变了原始对象,所以我使用了一个“技巧”来先制作一个深拷贝。 keep_entries_with
生成一个具有原始形状的对象作为您的输入,但由于您的输出形状不同,第二步只是将过滤后的结果转换为您想要的输出形状。
deep_copy = Marshal.load(Marshal.dump(hash_or_array))
def keep_entries_with(target, obj)
return unless obj.is_a?(Hash) || obj.is_a?(Array)
case obj
when Hash
keep_entries_with(target, obj.values)
obj.keep_if do |k, v|
v == target ||
v.is_a?(Hash) && v.values.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) } ||
v.is_a?(Array) && v.any? { _1 == target || _1.is_a?(Hash) || _1.is_a?(Array) }
end
when Array
obj.each do |v|
keep_entries_with(target, v)
end
end
end
filtered = keep_entries_with("buyer35", deep_copy)
final_result = filtered.first.map { |k, v| { k => v } }
pp final_result
产生:
[{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}},
{"book4"=>["buyer41", "buyer42", "buyer43", "buyer35"]}]