在Ruby中递归遍历哈希

Dex*_*Dex 4 ruby

我遇到了遍历Hash的这个函数的问题.哈希可能包含一个哈希数组.我希望该方法搜索一个id,然后只返回它找到的嵌套哈希.

它似乎适用于遍历,但它返回传入的原始值.

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')
Run Code Online (Sandbox Code Playgroud)

sep*_*p2k 11

当您以find_by_id递归方式调用时,您没有对返回值执行任何操作.您需要检查是否找到了某些内容,如果是,则返回该内容,即:

result = find_by_id(elm, find_this)
return result if result
Run Code Online (Sandbox Code Playgroud)

您还需要nil在方法结束时返回(在每个循环之后),因此nil如果未找到任何内容则返回.如果不这样做,它将返回其返回值,each即您迭代的哈希值.

编辑:

以下是我概述的更改的完整代码:

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
Run Code Online (Sandbox Code Playgroud)

EDIT2:

另一种我觉得更清晰的方法是将用于迭代结构的逻辑与用于查找具有正确id的元素的逻辑分开:

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
Run Code Online (Sandbox Code Playgroud)

通过dfs返回,Enumerable我们可以使用该Enumerable#find方法,这使代码更简单.

如果您需要编写另一个需要以递归方式遍历哈希的方法,这也可以重用代码,因为您可以重用dfs方法.