Когда вы вызываете find_by_id
рекурсивно, вы ничего не делаете с возвращаемым значением. Вам нужно проверить, нашел ли он что-то и, если да, вернуть это, т.е.
result = find_by_id(elm, find_this)
return result if result
Вам также необходимо вернуть nil
в конце метода (после каждого цикла), поэтому он возвращает nil
, если ничего не было найдено. Если вы этого не сделаете, он вернет возвращаемое значение each
, то есть хеш, который вы перебрали.
Edit:
Вот полный код с изложенными мною изменениями:
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
result = find_by_id(elm, find_this)
return result if result
end
end
end
end
end
# Return nil if no match was found
nil
end
Edit2:
Альтернативный подход, который я считаю более чистым, состоит в том, чтобы отделить логику итерации структуры от логики для нахождения элемента с правильным идентификатором:
def dfs(hsh, &blk)
return enum_for(:dfs, hsh) unless blk
yield hsh
hsh.each do |k,v|
if v.is_a? Array
v.each do |elm|
dfs(elm, &blk)
end
end
end
end
def find_by_id(hsh, search_for)
dfs(hsh).find {|node| node["_id"] == search_for }
end
Заставив dfs
вернуть Enumerable
, мы можем использовать метод Enumerable#find
, который делает код немного проще.
Это также позволяет повторно использовать код, если вам когда-либо понадобится написать другой метод, которому необходимо рекурсивно перебирать хеш, поскольку вы можете просто повторно использовать метод dfs.