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_id
是nil
,那么它应该是父节点,如果parent_id
不是nil
,那么它应该在特定的父节点之下。
基于id
和parent_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 如何生成树结构形式的数组?的主要内容,如果未能解决你的问题,请参考以下文章