ruby 如何生成树结构形式的数组?

Posted

技术标签:

【中文标题】ruby 如何生成树结构形式的数组?【英文标题】:ruby how to generate a tree structure form array? 【发布时间】:2013-09-16 13:30:05 【问题描述】:

我有一个数组,其中包含这样的项目列表

arr = [
  :id=>1,  :title=>"A",      :parent_id=>nil, 
  :id=>2,  :title=>"B",      :parent_id=>nil,
  :id=>3,  :title=>"A1",     :parent_id=>1, 
  :id=>4,  :title=>"A2",     :parent_id=>1,
  :id=>5,  :title=>"A11",    :parent_id=>3, 
  :id=>6,  :title=>"12",     :parent_id=>3,
  :id=>7,  :title=>"A2=121", :parent_id=>6, 
  :id=>8,  :title=>"A21",    :parent_id=>4,
  :id=>9,  :title=>"B11",    :parent_id=>2, 
  :id=>10, :title=>"B12",    :parent_id=>2,
   ...
]

如果parent_idnil,那么它应该是父节点,如果parent_id 不是nil,那么它应该在特定的父节点之下。

基于idparent_id,我想提供这样的回复:

-A
  -A1
    -A11
    -A12
      -A123
  -A2
    -A21
-B
  -B1
    -B11
    -B12

如何生成上述回复?

谢谢

【问题讨论】:

这是一个 Rails 项目吗? 是的,它是一个 Rails 3.0.3 项目 你可以使用 github.com/stefankroes/ancestry 或 github.com/mceachen/closure_tree 这样的 gem,两者都可以生成嵌套哈希 感谢支持,是否可以在数据库中不添加任何字段的情况下生成它? Closure_tree 与 parent_id 列一起使用,您只需要设置一个额外的表并运行一个 rake 任务,请参阅 github.com/mceachen/closure_tree#installation 【参考方案1】:

您可以使用像 Closure_tree 这样的 gem:

hash_tree 提供了一种将子树渲染为有序的方法 嵌套哈希:

Tag.hash_tree
#=> a => b => c1 => d1 => , c2 => d2 => , b2 => 

或Ancestry:

Ancestry 可以将整个子树排列成嵌套散列,以便于 从数据库检索后导航。 TreeNode.arrange 可以 例如返回:

 #<TreeNode id: 100018, name: "Stinky", ancestry: nil>
  =>  #<TreeNode id: 100019, name: "Crunchy", ancestry: "100018">
    =>  #<TreeNode id: 100020, name: "Squeeky", ancestry: "100018/100019">
      => 
    
  

查看https://www.ruby-toolbox.com/categories/Active_Record_Nesting 了解其他宝石。

更新

如果你必须在内存中做,这样的事情应该可以工作:

nested_hash = Hash[arr.map|e| [e[:id], e.merge(children: [])]]
nested_hash.each do |id, item|
  parent = nested_hash[item[:parent_id]]
  parent[:children] << item if parent
end
tree = nested_hash.select  |id, item| item[:parent_id].nil? .values

require 'pp'
pp tree

输出

[:id=>1,
  :title=>"A",
  :parent_id=>nil,
  :children=>
   [:id=>3,
     :title=>"A1",
     :parent_id=>1,
     :children=>
      [:id=>5, :title=>"A11", :parent_id=>3, :children=>[],
       :id=>6,
        :title=>"12",
        :parent_id=>3,
        :children=>
         [:id=>7, :title=>"A2=121", :parent_id=>6, :children=>[]]],
    :id=>4,
     :title=>"A2",
     :parent_id=>1,
     :children=>[:id=>8, :title=>"A21", :parent_id=>4, :children=>[]]],
 :id=>2,
  :title=>"B",
  :parent_id=>nil,
  :children=>
   [:id=>9, :title=>"B11", :parent_id=>2, :children=>[],
    :id=>10, :title=>"B12", :parent_id=>2, :children=>[]]]

【讨论】:

这两个 gem 都非常棒,我们可以在没有任何数据库迁移的情况下完成它吗?【参考方案2】:

一个例子:

#!/usr/bin/env ruby

root = :id => 0, :title => '', :parent_id => nil

arr = arr = [
  :id=>1,  :title=>"A",      :parent_id=>nil, 
  :id=>2,  :title=>"B",      :parent_id=>nil,
  :id=>3,  :title=>"A1",     :parent_id=>1, 
  :id=>4,  :title=>"A2",     :parent_id=>1,
  :id=>5,  :title=>"A11",    :parent_id=>3, 
  :id=>6,  :title=>"12",     :parent_id=>3,
  :id=>7,  :title=>"A2=121", :parent_id=>6, 
  :id=>8,  :title=>"A21",    :parent_id=>4,
  :id=>9,  :title=>"B11",    :parent_id=>2, 
  :id=>10, :title=>"B12",    :parent_id=>2,
]

map = 

arr.each do |e|
  map[e[:id]] = e
end

@@tree = 

arr.each do |e|
  pid = e[:parent_id]
  if pid == nil || !map.has_key?(pid)
    (@@tree[root] ||= []) << e
  else
    (@@tree[map[pid]] ||= []) << e
  end
end

def print_tree(item, level)
  items = @@tree[item]
  unless items == nil
    indent = level > 0 ? sprintf("%#level * 2s", " ") : ""
    items.each do |e|
      puts "#indent-#e[:title]"
      print_tree(e, level + 1)
    end
  end
end

print_tree(root, 0)

输出:

-A
  -A1
    -A11
    -12
      -A2=121
  -A2
    -A21
-B
  -B11
  -B12

【讨论】:

【参考方案3】:

并不是要替换经过验证的宝石,但根据您的需要,您可以使用以下简单的东西:

groups = arr.group_by |x| x[:parent_id] 
groups.default = []

build_tree = 
  lambda do |parent| 
    [parent[:title], groups[parent[:id]].map(&build_tree)]
    # or
    #  parent[:title] => groups[parent[:id]].map(&build_tree) 
  end

p build_tree[:id => nil][1] # :id => nil is not required, empty hash will work too
# => [["A", [["A1", [["A11", []], ["A12", [["A122", []]]]]], ["A2", [["A21", []]]]]], ["B", [["B11", []], ["B12", []]]]]

【讨论】:

感谢您抽出宝贵时间,让我难以理解 build_tree 背后的概念。

以上是关于ruby 如何生成树结构形式的数组?的主要内容,如果未能解决你的问题,请参考以下文章

急!数据结构最小生成树prim算法C语言实现

急!数据结构最小生成树prim算法C语言实现

如何从数据库中的表生成树结构?

图的最小生成树算法(Prim和Kruskal)

通用的树型类,可以生成任何树型结构

树数据结构