[英]Create a tree from a list of strings containing paths of files - javascript
假设我有以下数组:
[
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue"
]
我们使用每个子文件夹中的Index.vue
作为该文件夹的父文件夹。 这意味着上面看起来像:
[
{
name: "About",
children: []
},
{
name: "Categories",
children:
[
{
name: "Index.vue",
children: []
},
{
name: "Demo.vue",
children: []
},
{
name: "Flavors.vue",
children: []
}
]
}
]
通过使用以下教程,我能够让它稍微工作: https ://joelgriffith.net/array-reduce-is-pretty-neat/
然而,它是一个根对象,每个文件都有一个属性,而不是一个数组,每个文件都有一个对象。
以下代码产生预期的输出:
let paths = [ "About.vue", "Categories/Index.vue", "Categories/Demo.vue", "Categories/Flavors.vue" ]; let helper = { index: -1, name: "" }; function treeify(files) { var fileTree = []; function mergePathsIntoFileTree(prevDir, currDir, i, filePath) { helper.name = currDir; helper.index = i; if (helper.index == 0) { let index = prevDir.findIndex(x => x.name == helper.name); if (index < 0) { prevDir.push({ name: helper.name, children: [] }); } return prevDir; } if (helper.index >= 0) { let obj = { name: currDir, children: [] }; prevDir[helper.index].children.push(obj); helper.index = i; helper.name = currDir; } } function parseFilePath(filePath) { var fileLocation = filePath.split('/'); // If file is in root directory, eg 'index.js' if (fileLocation.length === 1) { fileTree[0] = { name: fileLocation[0], children: [] }; } else { fileLocation.reduce(mergePathsIntoFileTree, fileTree); } } files.forEach(parseFilePath); return fileTree; } console.log(treeify(paths));
但是,它在以下输入时失败:
let paths = [
"About.vue",
"Categories/Index.vue",
"Categories/Demo.vue",
"Categories/Flavors.vue",
"Categories/Types/Index.vue",
"Categories/Types/Other.vue"
];
有谁知道让它适用于进一步嵌套的路径列表的解决方案?
您可以使用forEach
方法创建此结构以循环每个路径并将其拆分为/
上的数组,然后您还可以使用reduce
方法创建嵌套对象。
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue"]; let result = []; let level = {result}; paths.forEach(path => { path.split('/').reduce((r, name, i, a) => { if(!r[name]) { r[name] = {result: []}; r.result.push({name, children: r[name].result}) } return r[name]; }, level) }) console.log(result)
您可以对每个找到的名称部分采用迭代方法并获取一个对象并返回子项以进行下一次搜索。
var paths = ["About.vue", "Categories/Index.vue", "Categories/Demo.vue", "Categories/Flavors.vue", "Categories/Types/Index.vue", "Categories/Types/Other.vue"], result = paths.reduce((r, p) => { var names = p.split('/'); names.reduce((q, name) => { var temp = q.find(o => o.name === name); if (!temp) q.push(temp = { name, children: [] }); return temp.children; }, r); return r; }, []); console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
因此,首先,我将假设这是在 Node.js 中,其次,我目前在家,所以我目前无法访问 node.js,所以我没有真正的方法来测试代码,但是以下代码应该可以工作。
您需要做的是检查文件夹的内容,然后检查文件夹中的项目是否为目录,如果为真,则使用新路径(也称为递归)再次调用该函数。
因此,首先您从读取文件夹开始,将每个项目的名称添加到对象的.name
属性中,然后检查它是否是文件夹,如果是,则对该路径进行递归。 继续返回一个对象数组(这将被添加到.children
属性中。
var fs = require('fs');
var filetree = DirToObjectArray('path/to/folder/');
function DirToObjectArray(path) {
var arr = [];
var content = fs.readdirSync(path, { withFileTypes: true });
for (var i=0; i< content.length; i++) {
var obj = new Object({
name: "",
children: []
});
obj.name = content[i].name;
if (content[i].isDirectory()) {
obj.children = DirToObjectArray(path + content[i].name + "/");
}
arr.push(obj);
}
return arr;
}
如果您不使用 node.js 而是使用浏览器中的 javascript,我无法帮助您
我接受了@Nenad Vracar的回答(并赞成,谢谢!),但我也需要在我的用例中允许重复的文件名。 我只是想分享我是如何做到的。
let paths = ["About.vue","Categories/Index.vue","Categories/Demo.vue","Categories/Flavors.vue","Categories/Types/Index.vue","Categories/Types/Other.vue","Categories/Types/Other.vue","Categories/Types/Other.vue"]; let result = []; let level = {result}; paths.forEach(path => { path.split('/').reduce((r, name, i, a) => { if(!r[name]) { r[name] = {result: []}; r.result.push({name, children: r[name].result}); } else if (i === a.length - 1) { // Allow duplicate filenames. // Filenames should always be at the end of the array. r.result.push({name, children: []}); } return r[name]; }, level) }) console.log(result)
以下解决方案源自@nenad-vracar 的回答。 他回答的一个缺点是,如果路径包含“结果”,则代码将失败。 一个简单的解决方法是将“result”重命名为“”,即包含不能出现在路径中的字符。
export interface IPathNode {
name: string;
children: IPathNode[];
path: IPath | null;
}
export interface IPath {
key: string;
directory: boolean;
}
interface IPathLevel {
// ["<result>"]: IPathNode[];
[key: string]: IPathLevel | IPathNode[];
}
export const createPathTree = (paths: IPath[]): IPathNode | null => {
const level: IPathLevel = { ["<result>"]: [] as IPathNode[] };
paths.forEach((path) => {
path.key.split("/").reduce(
((
currentLevel: IPathLevel,
name: string,
index: number,
array: string[]
) => {
if (!currentLevel[name]) {
currentLevel[name] = { ["<result>"]: [] };
(currentLevel["<result>"] as IPathNode[]).push({
name,
children: (currentLevel[name] as IPathLevel)[
"<result>"
] as IPathNode[],
/* Attach the path object to the leaf node. */
path: index === array.length - 1 ? path : null,
});
}
return currentLevel[name];
}) as any,
level
);
});
const finalArray = level["<result>"] as IPathNode[];
return finalArray.length > 0 ? finalArray[0] : null;
};
console.log(
JSON.stringify(
createPathTree([
{
key: "/components/button.tsx",
directory: false,
},
{
key: "/components/checkbox.tsx",
directory: false,
},
{
key: "/result",
directory: true,
},
]),
null,
4
)
);
输出:
{
"name": "",
"children": [
{
"name": "components",
"children": [
{
"name": "button.tsx",
"children": [],
"path": {
"key": "/components/button.tsx",
"directory": false
}
},
{
"name": "checkbox.tsx",
"children": [],
"path": {
"key": "/components/checkbox.tsx",
"directory": false
}
}
],
"path": null
},
{
"name": "result",
"children": [],
"path": {
"key": "/result",
"directory": true
}
}
],
"path": null
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.