从文件路径构建树
Posted
技术标签:
【中文标题】从文件路径构建树【英文标题】:Build a tree from file paths 【发布时间】:2015-09-12 08:35:14 【问题描述】:我正在尝试从文件路径创建一个树形视图,可以动态添加和删除它,例如:
A/B/C/D/file1.txt
A/B/D/E/file2.txt
A/B/D/G/file3.txt
A/B/D/G/file4.txt
然而,我的树要求没有子项(文件)的路径应该折叠在一个节点中。对于上面的路径,它将产生:
A/B
|---C/D
file1.txt
|---D
|---E
| file2.txt
|---G
file3.txt
file4.txt
有什么想法吗?创建树很容易,但我无法超越那个额外的条件......我假设我必须使用某种递归来添加项目并打破路径,因为我们发现某个路径有更多的孩子(然后递归地做同样的事情?)。我应该使用某种特里吗?当同一个路径可以有多个文件时它会工作吗?...谢谢!
【问题讨论】:
我认为你应该像往常一样构建树,让你的 UI 处理额外的条件,拆分这些要求可能更容易;) 很遗憾,我不能。 【参考方案1】:让我们从一个简单的解决方案开始,按原样打印树:
function browseTree(node)
// ...print node...
// Visit recursively the children nodes:
for (var child: node.children)
browseTree(child);
现在,让我们修改它以“缩短”单文件夹路径:
function browseTree(node)
// Before printing, accumulate as many straight folders as possible:
var nodeName=node.name
while (hasJustOneFolder(node))
// This loop steps deeper in the tree:
node=node.children[0]
nodeName+="/"+node.name;
// ...print node...
// Last, visit recursively the non-unique children nodes:
for (var child: node.children)
browseTree(child);
function hasJustOneFolder(node)
return node.children.length==1 && node.children[0].isFolder();
【讨论】:
【参考方案2】:鉴于您的要求,似乎将新文件添加到 C 中并不意味着递归操作。
如果将 file5.txt
添加到文件夹 C,则必须在具有 2 个子节点的 C
中转换 C/D
:file5.txt
和一个名为 D
的新节点。 D
将拥有与旧节点 C/D
相同的子节点。然后你可以擦除节点C/D
。
但是,这不会影响节点 A/B
,因为文件夹 A 仍然只有一个文件夹 (B) 作为子文件夹。因此,您可以解决仅进行本地更改的问题。
【讨论】:
【参考方案3】:我使用Map
创建了一个具有自定义树节点格式的示例代码,打印函数是一个生成器函数,用于逐行获取树的路径。
// Node
class NodePath
constructor(e)
this.isFolder = e.isFolder;
this.name = e.name;
this.childs = new Map();
// Make path tree
function makePathsTree(paths)
const treeRoot = new NodePath(isFolder: true, name: "*");
for (const path of paths)
// Set current post as root
let curPos = treeRoot;
// For each part
const parts = path.split("/");
while (parts.length)
// Get cur
const curPart = parts.shift();
// Get child node, create if not exists
let childNode = curPos.childs.get(curPart);
if (!childNode)
childNode = new NodePath(
isFolder: !!parts.length,
name: curPart,
);
curPos.childs.set(curPart, childNode)
// Update cur post to child node
curPos = childNode;
// Return tree
return treeRoot;
// Generator function prevent huge large file system strings
function *printPathsTree(node, offset = 0, prev = "")
// Offset str
const offsetStr = " ".repeat(offset);
// Is folder
if (!node.isFolder)
yield `$offsetStr$prev$node.name`;
return;
// If one child and is folder, merge paths
if (node.childs.size === 1)
const child = node.childs.values().next().value;
if (child.isFolder === true)
for (const childData of printPathsTree(child, offset, `$prev$node.name/`))
yield childData;
return;
// Print node name
yield `$offsetStr$prev$node.name`;
// For each child, print data inside
for (const child of node.childs.values())
for (const childData of printPathsTree(child, offset + prev.length, "|---"))
yield childData;
// == CODE ==
console.log("WITH ROOT:");
const tree = makePathsTree([
"A/B/C/D/file1.txt",
"A/B/C/D/file2.txt",
"A/B/D/E/file2.txt",
"A/B/D/G/file3.txt",
"A/B/D/G/file4.txt",
]);
// Print tree step by step
for(const nodePath of printPathsTree(tree))
console.log(nodePath);
// Print with A as root
console.log("\nA AS ROOT:");
for(const nodePath of printPathsTree(tree.childs.values().next().value))
// for(const nodePath of printPathsTree(tree.childs.get("A"))) // same
console.log(nodePath);
输出:
WITH ROOT:
*/A/B
|---C/D
|---file1.txt
|---file2.txt
|---D
|---E
|---file2.txt
|---G
|---file3.txt
|---file4.txt
A AS ROOT:
A/B
|---C/D
|---file1.txt
|---file2.txt
|---D
|---E
|---file2.txt
|---G
|---file3.txt
|---file4.txt
【讨论】:
【参考方案4】:您可以通过在前导路径名称上递归分组来构建树,然后如果父母只有一个孩子,则合并父子名称:
var paths = ["A/B/C/D/file1.txt", "A/B/C/D/file2.txt", "A/B/D/E/file2.txt", "A/B/D/G/file3.txt", "A/B/D/G/file4.txt"]
function merge_paths(paths)
var d = ;
var new_d =
for (var [a, ...b] of paths)
d[a] = (a in d) ? [...d[a], b] : [b]
for (var i of Object.keys(d))
if (d[i].every(x => x.length === 1))
new_d[i] = d[i].map(x => x[0]);
else
var k = merge_paths(d[i])
if (Object.keys(k).length > 1)
new_d[i] = k
else
new_d[`$i/$Object.keys(k)[0]`] = k[Object.keys(k)[0]]
return new_d;
var result = merge_paths(paths.map(x => x.split('/')))
console.log(result)
【讨论】:
以上是关于从文件路径构建树的主要内容,如果未能解决你的问题,请参考以下文章
从列表 os 文件路径 (Python) 构造树 - 取决于性能