I’m having a problem with this function that traverses a Hash. The Hash may contain an Array of Hashes. I want the method to search for an id and then return just the nested hash it finds.
It seems to work for the traversal, but it returns the original value passed in.
require 'rubygems'
require 'ruby-debug'
def find_by_id(node, find_this="")
if node.is_a?(Hash)
node.each do |k,v|
if v.is_a?(Array)
v.each do |elm|
if elm["_id"] == find_this && !find_this.empty?
return elm # THIS IS WHAT I WANT!
else
find_by_id(elm, find_this)
end
end
end
end
end
end
x = {"name" => "first", "_id"=>'4c96a9a56f831b0eb9000005', "items"=>["name" => "second", "_id"=>'4c96a9af6f831b0eb9000009', "others"=>[{"name" => "third", "_id"=>'4c96a9af6f831b0eb9000007'}, {"name" => "fourth", "_id"=>'4c96a9af6f831b0eb9000008'}] ] }
find_by_id(x, '4c96a9af6f831b0eb9000008')
When you invoke
find_by_idrecursively, you’re not doing anything with the return value. You need to check whether it found something and if so return that, i.e.:You also need to return
nilat the end of the method (after the each loop), so it returnsnilif nothing was found. If you don’t, it’ll return the return value ofeachwhich is the hash that you iterated over.Edit:
Here’s the full code with the changes I outlined:
Edit2:
An alternative approach, that I find cleaner, is to separate the logic for iterating the structure from the logic for finding the element with the right id:
By making
dfsreturn anEnumerablewe can use theEnumerable#findmethod, which makes the code a bit simpler.This also enables code reuse if you ever need to write another method that needs to iterate through the hash recursively, as you can just reuse the dfs method.