我有以下哈希数组:
[
{key: 1, reference: 'reference', path: '.../public/shared/file_1.txt', type: 'public'},
{key: 2, reference: 'reference', path: '.../public/shared/file_2.txt', type: 'public'},
{key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt', type: 'public'},
{key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt', type: 'public'},
{key: 5, reference: 'reference', path: '.../log/file_5.txt', type: 'log'},
{key: 5, reference: 'reference', path: '.../tmp/cache/file_6.txt', type: 'log'},
]
然后使用每个节点的路径结构,我想创建一个这样的树:
[
{
key: 'root', folder: true, title: 'root',
children: [
{
key: 'public', folder: true, title: 'public',
children: [
{
key: 'shared', folder: true, title: 'shared',
children: [
{ key: 1, title: 'file_1.txt' },
{ key: 2, title: 'file_2.txt' },
{
key: 'sub_folder', folder: true, title: 'sub_folder',
children: [
{ key: 3, title: 'file_3.txt' },
{ key: 4, title: 'file_4.txt' }
]
}
]
}
]
},
{
key: 'log', folder: true, title: 'log', children: [ { key: 5, title: 'file_5.txt' } ]
},
{
key: 'tmp', folder: true, title: 'tmp',
children: [
{ key: 'cache', folder: true, title: 'cache', children: [ { key: 6, title: 'file_6.txt' } }
]
}
]
}
]
是否可以使用每个节点的路径创建该结构?我使用Ruby on Rails。
答案 0 :(得分:1)
您可以像online repl一样定义类:
class Tree
attr_reader :array, :tree
def initialize(array)
@array = array
create_tree!
end
private
def create_tree!
@tree = []
array.each do |hash|
process_path(hash[:path].gsub('...', 'root').split('/'))
end
end
def process_path(array)
current = @tree
array.each do |folder_or_file|
if persist = current.find { |hash| hash[:key] == folder_or_file }
current = persist[:children]
else
current << {
key: folder_or_file,
folder: folder_or_file['.'].nil?,
title: folder_or_file,
children: []
}
current = current.last[:children]
end
end
end
end
arr = [
{key: 1, reference: 'reference', path: '.../public/shared/file_1.txt', type: 'public'},
{key: 2, reference: 'reference', path: '.../public/shared/file_2.txt', type: 'public'},
{key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt', type: 'public'},
{key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt', type: 'public'},
{key: 5, reference: 'reference', path: '.../log/file_5.txt', type: 'log'},
{key: 5, reference: 'reference', path: '.../tmp/cache/file_6.txt', type: 'log'},
]
Tree.new(arr).tree
答案 1 :(得分:0)
我提供了一个递归解决方案。
<强>代码强>
def doit(arr)
a = arr.map do |g|
*front, last = ['root', *g[:path][4..-1].split('/')]
[front, { key: g[:key], title: last }]
end
recurse a
end
def recurse(a)
a.reject { |dirs, _| dirs.empty? }.
group_by { |dirs,_| dirs.shift }.
map do |dir,v|
empty, non_empty = v.partition { |d,_| d.empty? }
{ key: dir, folder: true, title: dir,
children: [*empty.map(&:last), *recurse(non_empty)] }
end
end
示例强>
arr = [
{ key: 1, reference: 'reference', path: '.../public/shared/file_1.txt',
type: 'public' },
{ key: 2, reference: 'reference', path: '.../public/shared/file_2.txt',
type: 'public' },
{ key: 3, reference: 'reference', path: '.../public/shared/sub_folder/file_3.txt',
type: 'public' },
{ key: 4, reference: 'reference', path: '.../public/shared/sub_folder/file_4.txt',
type: 'public' },
{ key: 5, reference: 'reference', path: '.../log/file_5.txt',
type: 'log' },
{ key: 6, reference: 'reference', path: '.../tmp/cache/file_6.txt',
type: 'log' }
]
我们现在可以从arr
构建所需的数组:
doit arr
#=> [{:key=>"root", :folder=>true, :title=>"root", :children=>
# [{:key=>"public", :folder=>true, :title=>"public", :children=>
# [{:key=>"shared", :folder=>true, :title=>"shared", :children=>
# [{:key=>1, :title=>"file_1.txt"},
# {:key=>2, :title=>"file_2.txt"},
# {:key=>"sub_folder", :folder=>true, :title=>"sub_folder",
# :children=>[{:key=>3, :title=>"file_3.txt"},
# {:key=>4, :title=>"file_4.txt"}
# ]
# }
# ]
# }
# ]
# },
# {:key=>"log", :folder=>true, :title=>"log",
# :children=>[{:key=>5, :title=>"file_5.txt"}]
# },
# {:key=>"tmp", :folder=>true, :title=>"tmp",
# :children=>[{:key=>"cache", :folder=>true, :title=>"cache",
# :children=>[{:key=>6, :title=>"file_6.txt"}]
# }
# ]
# }
# ]
# }
# ]
<强>解释强>
步骤如下(对于示例中的arr
),
在doit
a = arr.map do |g|
*front, last = ['root', *g[:path][4..-1].split('/')]
[front, { key: g[:key], title: last }]
end
#=> [[["root", "public", "shared"], {:key=>1, :title=>"file_1.txt"}],
# [["root", "public", "shared"], {:key=>2, :title=>"file_2.txt"}],
# [["root", "public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
# [["root", "public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
# [["root", "log"], {:key=>5, :title=>"file_5.txt"}],
# [["root", "tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]]
打破这一点,我们执行以下计算。
arr
的第一个元素传递给map's block and becomes the value of the block variable
g`:
g = arr.first
#=> {:key=>1, :reference=>"reference",
# :path=>".../public/shared/file_1.txt", :type=>"public"}
然后执行块计算。
b = g[:path]
#=> ".../public/shared/file_1.txt"
c = b[4..-1]
#=> "public/shared/file_1.txt"
d = c.split('/')
#=> ["public", "shared", "file_1.txt"]
e = ['root', *d]
#=> ["root", "public", "shared", "file_1.txt"]
*front, last = e
#=> ["root", "public", "shared", "file_1.txt"]
front
#=> ["root", "public", "shared"]
last
#=> "file_1.txt"
f = g[:key]
#=>
[front, { key: f, title: last }]
#=> [["root", "public", "shared"], {:key=>1, :title=>"file_1.txt"}]
arr
的其余元素的映射类似。
上面的数组a
被传递到recurse。第一步是删除[d, h]
为空的任何元素d
(h
是一个目录数组,d
哈希)。在将一个或多个哈希值添加到数组:children
之后,这是一个需要更深入递归的技术要求。
m = a.reject { |dirs, _| dirs.empty? }
#=> a (no elements are removed)
下一步是[dirs, h]
的第一个元素m
的组元素dirs
。我在下面的块中使用dirs.shift
也从数组dirs
中删除了该元素。
n = m.group_by { |dirs,_| dirs.shift }
#=> {"root"=>[
# [["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
# [["public", "shared"], {:key=>2, :title=>"file_2.txt"}],
# [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
# [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
# [["log"], {:key=>5, :title=>"file_5.txt"}],
# [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
# ]
# }
n
的第一个元素现在传递给map
块并分配了块变量:
dir, v = n.first
#=> ["root", [
# [["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
# [["public", "shared"], {:key=>2, :title=>"file_2.txt"}],
# [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
# [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
# [["log"], {:key=>5, :title=>"file_5.txt"}],
# [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
# ]
# ]
dir
#=> "root"
v #=> [[["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
# ...
# [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
# ]
然后执行块计算。
empty, non_empty = v.partition { |d,_| d.empty? }
empty
#=> []
non_empty
#=> [[["public", "shared"], {:key=>1, :title=>"file_1.txt"}],
# [["public", "shared"], {:key=>2, :title=>"file_2.txt"}],
# [["public", "shared", "sub_folder"], {:key=>3, :title=>"file_3.txt"}],
# [["public", "shared", "sub_folder"], {:key=>4, :title=>"file_4.txt"}],
# [["log"], {:key=>5, :title=>"file_5.txt"}],
# [["tmp", "cache"], {:key=>6, :title=>"file_6.txt"}]
# ]
p = empty.map(&:last)
#=> []
{ key: dir, folder: true, title: dir,
children: [*p, *recurse(non_empty)] }
#=> { key: 'root', folder: true, title: 'root',
# children: [*[], *recurse(non_empty)] }
最后一个键:children
的值减少为[*recurse(non_empty)]
。如图所示,recurse
现在以递归方式调用,参数为non-empty
。
剩下的计算是相似的,但是当recurse
传递一个数组,其中有一个或多个元素dirs
数组包含单个元素时,事情会有所不同,导致相关的哈希被添加到一个数组,它是一个键:children
的值。要完全理解计算,可能需要在代码中添加一些puts
语句。