将扁平树解析为非扁平树的算法

发布于 2024-09-01 14:28:37 字数 1063 浏览 6 评论 0原文

我有以下扁平树:

id    name                        parent_id    is_directory
===========================================================
50    app                         0            1
31    controllers                 50           1
11    application_controller.rb   31           0
46    models                      50           1
12    test_controller.rb          31           0
31    test.rb                     46           0

我正在尝试找出一种算法,将其放入以下树结构中:

[{
  id: 50,
  name: app,
  is_directory: true
  children: [{
    id: 31,
    name: controllers,
    is_directory: true,
    children: [{
      id: 11,
      name: application_controller.rb
      is_directory: false
    },{
      id: 12,
      name: test_controller.rb,
      is_directory: false
    }],
  },{
    id: 46,
    name: models,
    is_directory: true,
    children: [{
      id: 31,
      name: test.rb,
      is_directory: false
    }]
  }]
}]

有人能给我指出正确的方向吗?我正在寻找步骤(例如,构建关联数组;循环遍历数组查找 x;等等)。

我使用的是 Ruby,因此我可以使用面向对象的语言功能。

I have the following flat tree:

id    name                        parent_id    is_directory
===========================================================
50    app                         0            1
31    controllers                 50           1
11    application_controller.rb   31           0
46    models                      50           1
12    test_controller.rb          31           0
31    test.rb                     46           0

and I am trying to figure out an algorithm for getting this into the following tree structuree:

[{
  id: 50,
  name: app,
  is_directory: true
  children: [{
    id: 31,
    name: controllers,
    is_directory: true,
    children: [{
      id: 11,
      name: application_controller.rb
      is_directory: false
    },{
      id: 12,
      name: test_controller.rb,
      is_directory: false
    }],
  },{
    id: 46,
    name: models,
    is_directory: true,
    children: [{
      id: 31,
      name: test.rb,
      is_directory: false
    }]
  }]
}]

Can someone point me in the right direction? I'm looking for steps (eg. build an associative array; loop through the array looking for x; etc.).

I'm using Ruby, so I have object-oriented language features at my disposal.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

红玫瑰 2024-09-08 14:28:37

在 ruby​​ 中,您应该能够使用哈希在线性时间 O(n) 内轻松完成此操作。

# Put all your nodes into a Hash keyed by id  This assumes your objects are already Hashes
object_hash = nodes.index_by {|node| node[:id]}
object_hash[0] = {:root => true}

# loop through each node, assigning them to their parents
object_hash.each_value {|node|
  continue if node[:root]
  children = object_hash[node[:parent_id]][:children] ||= []
  children << node
}

#then your should have the structure you want and you can ignore 'object_hash' variable
tree = object_hash[0]

In ruby, you should be able to easily do it in linear time O(n) with a Hash.

# Put all your nodes into a Hash keyed by id  This assumes your objects are already Hashes
object_hash = nodes.index_by {|node| node[:id]}
object_hash[0] = {:root => true}

# loop through each node, assigning them to their parents
object_hash.each_value {|node|
  continue if node[:root]
  children = object_hash[node[:parent_id]][:children] ||= []
  children << node
}

#then your should have the structure you want and you can ignore 'object_hash' variable
tree = object_hash[0]
甜味超标? 2024-09-08 14:28:37

我已经用递归和非递归研究了这个问题。我在这里放置了 2 个变体:
"parend_id" = "head_id" # 对于这些示例

递归:

require 'pp'

nodes = [{"id"=>"1", "name"=>"User №1 Pupkin1", "head_id"=>nil},
         {"id"=>"2", "name"=>"User №2 Pupkin2", "head_id"=>"1"},
         {"id"=>"3", "name"=>"User №3 Pupkin3", "head_id"=>"2"}]

def to_tree(nodes, head_id = nil)
  with_head, without_head = nodes.partition { |n| n['head_id'] == head_id }
  with_head.map do |node|
    node.merge('children' => to_tree(without_head, node['id']))
  end
end

pp to_tree(nodes)

优点:

  • 应该如此

缺点!:

  • 如果我们 >= 3000,Ruby 将失败节点(发生这种情况是因为当 ruby​​ 需要返回时,ruby 对点(函数)有堆栈限制)如果输出有“pp”,它将在 >= 上失败200 个节点

非递归,有循环:

require 'pp'

nodes = [{"id"=>"1", "name"=>"User №1 Pupkin1", "head_id"=>nil},
         {"id"=>"2", "name"=>"User №2 Pupkin2", "head_id"=>"1"},
         {"id"=>"3", "name"=>"User №3 Pupkin3", "head_id"=>"2"}]

def to_tree(data)
  data.each do |item|
    item['children'] = data.select { |_item| _item['head_id'] == item['id'] }
  end
  data.select { |item| item['head_id'] == nil }
end

pp to_tree(nodes)

优点:

  • 更多 ruby​​ 风格

缺点:

  • 我们修改了 self 对象,这还不够好。

两种方式的结果都是:

[{"id"=>"1",
  "name"=>"User №1 Pupkin1",
  "head_id"=>nil,
  "children"=>
   [{"id"=>"2",
     "name"=>"User №2 Pupkin2",
     "head_id"=>"1",
     "children"=>
      [{"id"=>"3",
        "name"=>"User №3 Pupkin3",
        "head_id"=>"2",
        "children"=>[]}]}]}]

Resume

对于生产来说,最好使用第二种方式,可能有更优化的方式来实现它。
希望写的会有用

I had investigate the issue, with recursive and non recursive. I put here 2 variants:
"parend_id" = "head_id" # for those examples

Recursivly:

require 'pp'

nodes = [{"id"=>"1", "name"=>"User №1 Pupkin1", "head_id"=>nil},
         {"id"=>"2", "name"=>"User №2 Pupkin2", "head_id"=>"1"},
         {"id"=>"3", "name"=>"User №3 Pupkin3", "head_id"=>"2"}]

def to_tree(nodes, head_id = nil)
  with_head, without_head = nodes.partition { |n| n['head_id'] == head_id }
  with_head.map do |node|
    node.merge('children' => to_tree(without_head, node['id']))
  end
end

pp to_tree(nodes)

Pros:

  • as it should be

Cons!:

  • Ruby will fail if we will have >= 3000 nodes (this happen because ruby has stack limitation for points (functions) when ruby need retuns back) If you have 'pp' for the output it will fails on >= 200 nodes

Non recursevly, with cycle:

require 'pp'

nodes = [{"id"=>"1", "name"=>"User №1 Pupkin1", "head_id"=>nil},
         {"id"=>"2", "name"=>"User №2 Pupkin2", "head_id"=>"1"},
         {"id"=>"3", "name"=>"User №3 Pupkin3", "head_id"=>"2"}]

def to_tree(data)
  data.each do |item|
    item['children'] = data.select { |_item| _item['head_id'] == item['id'] }
  end
  data.select { |item| item['head_id'] == nil }
end

pp to_tree(nodes)

Pros:

  • more ruby style

Cons:

  • we modifies self object, that is not enough good.

The result of the both ways is:

[{"id"=>"1",
  "name"=>"User №1 Pupkin1",
  "head_id"=>nil,
  "children"=>
   [{"id"=>"2",
     "name"=>"User №2 Pupkin2",
     "head_id"=>"1",
     "children"=>
      [{"id"=>"3",
        "name"=>"User №3 Pupkin3",
        "head_id"=>"2",
        "children"=>[]}]}]}]

Resume

For production it is better to use second way, probably there's a more optimal way to realize it.
Hope written will be useful

记忆で 2024-09-08 14:28:37
  1. 构建一个堆栈并用根元素填充它。
  2. While(堆栈中有元素):
  3. 从堆栈中弹出一个元素并将其添加到树中它所属的位置。
  4. 在数组中找到该元素的所有子元素并将它们推入堆栈。

要将元素添加到树中(步骤 3),您需要首先找到它们的父元素。树数据结构应该允许您很快地做到这一点,或者您可以使用包含按 id 索引的树节点的字典。

如果您提到您正在使用哪种语言,则可以建议更具体的解决方案。

  1. Build a stack and populate it with the root element.
  2. While (there are elements in the stack):
  3. Pop an element off the stack and add it to where it belongs in the tree.
  4. Find all children of this element in your array and push them into the stack.

To add element to the tree (step 3), you 'd need to find their parent first. A tree data structure should allow you to do that pretty quickly, or you can use a dictionary that contains tree nodes indexed by id.

If you mention which language you 're using a more specific solution could be suggested.

若水微香 2024-09-08 14:28:37

以下是我必须对 @daniel-beardsley 的回复进行的一些更改,以使其对我有用。

1)由于我是从 activeRecord 关系开始的,所以我首先执行“as_json”来转换为哈希值。请注意,所有键都是字符串,而不是符号。

2) 在我的例子中,没有父项的项的父值为 nil 而不是 0。

3) 我在“继续”表达式上遇到编译错误,所以我将其更改为“下一个”(有人可以向我解释一下吗< /strong> - 也许这是 @daniel-beardsley 在转换为 ruby​​ 时出现的拼写错误?)

4)我在删除父项的项目时遇到了一些崩溃。我添加了代码来忽略这些 - 如果您愿意,您也可以将其放在根目录中

object_hash = myActiveRecordRelation.as_json.index_by {|node| node["id"]}
object_hash[nil] = {:root => true}

object_hash.each_value {|node|
  next if node[:root]
  next if  node["parent_id"] &&  !object_hash[node["parent_id"]] # throw away orphans

  children = object_hash[node["parent_id"]][:children] ||= []
  children << node
}

tree = object_hash[nil]

Here are a few changes I had to make to @daniel-beardsley's response to make it work for me.

1) Since I was starting with an activeRecord relation, I started by doing a "as_json"to convert to a hash. Note that all the keys were therefore strings, not symbols.

2) in my case items without parents had a parent value of nil not 0.

3) I got a compile error on the "continue" expression, so I changed that to "next" (can someone explain this to me -- maybe it was a typo by @daniel-beardsley when converting to ruby?)

4) I was getting some crashes for items with deleted parents. I added code to ignore these -- you could also put at the root if you prefer

object_hash = myActiveRecordRelation.as_json.index_by {|node| node["id"]}
object_hash[nil] = {:root => true}

object_hash.each_value {|node|
  next if node[:root]
  next if  node["parent_id"] &&  !object_hash[node["parent_id"]] # throw away orphans

  children = object_hash[node["parent_id"]][:children] ||= []
  children << node
}

tree = object_hash[nil]
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文