【问题标题】:Looking for smarter way to build directory tree寻找更聪明的方式来构建目录树
【发布时间】:2020-05-16 14:40:30
【问题描述】:

我有大型 Json 对象。该对象描述了其组件对象如何分层连接的树型关系。对象知道它的孩子是谁,但不(直接)知道它的父母是谁。下面的“my_hash”举例说明了该结构。每个对象都有一个 id 101、102 等,一个名称“one”、“two”等,它可以有 0、1 或多个子对象。我正在尝试为每个对象构建“路径”。例如。作为代码的结果,对象名称“五”的结果应该具有“/一/二/四”的路径。基本上我正在尝试构建一种对象层次结构的目录结构。

下面的代码可以工作,但它看起来很长,不是很优雅,也不是很Ruby。 对于如何更有效、更优雅地执行此操作的建议,我将不胜感激。而且我有预感,我的代码可能不是很健壮,即很好地处理异常。 任何想法或帮助表示赞赏。

顺便说一句,我只是在学习 Ruby,到目前为止主要使用 Perl 进行编程。

class Tree
  def initialize
    @my_hash = {
      101 => ["one", [102, 107]],
      102 => ["two", [103, 104]],
      103 => ["three", []],
      104 => ["four", [105, 106]],
      105 => ["five", []],
      106 => ["six", []],
      107 => ["seven", [108]],
      108 => ["eight", []],
    }
    @child_to_parent_node = {}
    @id_to_name = {}
    @my_path_hash = {}
    @my_hash.keys.each do |key|
      @my_path_hash[key] = ""
    end
    @parent_path_id = []
  end

  def map_child_to_parent
    @my_hash.each do |key, value|
      @id_to_name.store(key, value[0])
      node_name, children = value[0], value[1]
      children.each do |child_id|
        @child_to_parent_node.store(child_id, node_name)
      end
    end
  end

  def build_path(id)
    parent = @child_to_parent_node[id]
    parent.nil? ? return : @parent_path_id << parent
    id = @id_to_name.key(parent)
    build_path(id)
    @parent_path_id
  end

  def update_tree
    @id_to_name.keys.each do |id|
      tmp_array = self.build_path(id)
      path = ""
      if (tmp_array.nil?)
        path = "/"
      else
        tmp_array.reverse.each do
          path = path + "/" + tmp_array.pop
        end
      end
      puts "id: #{id} path: #{path}"
    end
  end
end

my_tree = Tree.new
my_tree.map_child_to_parent
my_tree.update_tree

【问题讨论】:

  • 附带说明,代码在“irb”中运行,但导致 vscode 调试器在更新树方法中的“tmp_array = self.build_path(id)”行挂起。我不知道为什么。任何人使用相同的环境并看到相同的问题?

标签: json ruby hash directory-structure


【解决方案1】:

事实上,要解决这个任务,你必须从叶子遍历树到根,对吧?因此,对于这个特定任务,您对树的表示非常不方便。如果您可以用一些内存来换取更干净的解决方案,我会创建一个包含每个节点的父节点的辅助结构。比方说

@parents = @my_hash.each_with_object({}) do |(pid, props), acc|
  _, children = props
  children.each { |cid| acc[cid] = pid }
end

#=> {102=>101, 107=>101, 103=>102, 104=>102, 105=>104, 106=>104, 108=>107}

现在,该任务可以通过几个辅助功能以非常简洁的方式解决。例如:

def id_by_name(name)
  id, _ = @my_hash.find { |k, v| v.first == name }
  id
end

def name_by_id(id)
  @my_hash[id].first
end

def path_to(node)
  path = [node]
  id = id_by_name(node)
  path.unshift(name_by_id(id)) while id = @parents[id]
  path.join("/")
end

path_to "five" #=> "one/two/four/five"

请注意:该解决方案仍然非常低效 - 主要是因为要通过其名称获取节点的 id,我们必须在最坏的情况下遍历整个初始哈希。这是我们为不能很好地适应任务的数据结构付出的代价。

【讨论】:

  • 您好康斯坦丁,感谢您抽出宝贵时间回答我的帖子。我无法控制传入的 Json 结构。但你的代码肯定有助于让事情变得更加整洁和高效!!
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2010-12-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-01
  • 2012-07-13
  • 1970-01-01
相关资源
最近更新 更多