[英]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.