Обход хэша в Ruby рекурсивно - PullRequest
       2

Обход хэша в Ruby рекурсивно

4 голосов
/ 20 сентября 2010

У меня проблема с этой функцией, которая пересекает хэш.Хэш может содержать массив хэшей.Я хочу, чтобы метод искал идентификатор, а затем возвращал только найденный вложенный хеш.

Кажется, это работает для обхода, но возвращает исходное значение, переданное в.

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')

1 Ответ

11 голосов
/ 20 сентября 2010

Когда вы вызываете 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.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...