Say I have the below ruby hash nested
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"],
"book5" => {
"book5,1" => "buyer5"
}
}]
And I want to search for a string that matches buyer35
. On match, I want it to return the following result
"book3" => {
"3" => [{
"6" => [{
"7" => "buyer35"
}]
}]
}]
All, other non matching keys,values, arrays should be omitted. I have the following example, but it doesn't quite work
def search(hash)
hash.each_with_object({}) do |(key, value), obj|
if value.is_a?(Hash)
returned_hash = search(value)
obj[key] = returned_hash unless returned_hash.empty?
elsif value.is_a?(Array)
obj[key] = value if value.any? { |v| matches(v) }
elsif matches(key) || matches(value)
obj[key] = value
end
end
end
def matches(str)
match_criteria = /#{Regexp.escape("buyer35")}/i
(str.is_a?(String) || str == true || str == false) && str.to_s.match?(match_criteria)
end
....
=> search(hash_or_array)
Any help is appreciated. I realize, I need to use recursion, but can't quite figure how to build/keep track of the matched node from the parent node.
CodePudding user response:
You can use the following recursive method.
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
recurse(hash_or_array, "buyer35")
#=> [{"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}]
recurse(hash_or_array, "buyer24")
#=>[{"book2"=>{"book22"=>"buyer24"}}]
recurse(hash_or_array, "buyer33")
#=> [{"book3"=>{"2"=>"buyer33"}}]
recurse(hash_or_array, 11)
#=> [{"book3"=>{"3"=>[{"5"=>11}]}}]
recurse(hash_or_array, "buyer5")
#=>[{"book5"=>{"book5,1"=>"buyer5"}}]
If desired, one may write, for example,
recurse(hash_or_array, "buyer35").first
#=> {"book3"=>{"3"=>[{"6"=>[{"7"=>"buyer35"}]}]}}