Reputation: 33
I am trying to create a file hierarchy tree from an array of strings, however I am not getting it quite right and I know it is terribly inefficient, but not sure how to make it better.
Given I have:
const paths = [
"test_data/new_directory/ok.txt",
"test_data/reads_1.fq",
"test_data/test_ref.fa",
"test_data/dir2/dir3/dir4/dir5/file1.txt",
"test_data/dir2/dir3/dir4/dir5/file2.txt",
"test_data/new_directory/file2.txt",
"other_dir/dir2/newfile.xls",
"other_dir/sub_dir/file1.xls",
"other_dir/sub_dir/file2.xls",
"third_dir/first.xls"
];
I would like to end up with the following object:
{
"/other_dir": {
path: "/other_dir",
type: "folder",
isRoot: true,
children: ["/other_dir/dir2"]
},
"/other_dir/dir2": {
path: "/other_dir/dir2",
type: "folder",
children: ["/other_dir/dir2/newfile.xls"]
},
"/other_dir/dir2/newfile.xls": {
path: "/other_dir/dir2/newfile.xls",
type: "file",
children: []
},
"/other_dir/sub_dir": {
path: "/other_dir/sub_dir",
type: "folder",
children: ["/other_dir/sub_dir/file1.xls", "/other_dir/sub_dir/file2.xls"]
},
"/other_dir/sub_dir/file1.xls": {
path: "/other_dir/sub_dir/file1.xls",
type: "file",
children: []
},
"/other_dir/sub_dir/file2.xls": {
path: "/other_dir/sub_dir/file2.xls",
type: "file",
children: []
},
"/test_data": {
path: "/test_data",
type: "folder",
isRoot: true,
children: [
"/test_data/dir2",
"/test_data/new_directory",
"/test_data/test_ref.fa",
"/test_data/reads_1.fq"
]
},
"/test_data/dir2": {
path: "/test_data/dir2",
type: "folder",
children: ["/test_data/dir2/dir3", "/test_data/file2.txt"]
},
"/test_data/file2.txt": {
path: "/test_data/file2.txt",
type: "file",
children: []
},
"/test_data/dir2/dir3": {
path: "/test_data/dir2/dir3",
type: "folder",
children: ["/test_data/dir2/dir3/dir4"]
},
"/test_data/dir2/dir3/dir4": {
path: "/test_data/dir2/dir3/dir4",
type: "folder",
children: ["/test_data/dir2/dir3/dir4/dir5"]
},
"/test_data/dir2/dir3/dir4/dir5": {
path: "/test_data/dir2/dir3/dir4/dir5",
type: "folder",
children: [
"/test_data/dir2/dir3/dir4/dir5/file1.txt",
"/test_data/dir2/dir3/dir4/dir5/file2.txt"
]
},
"/test_data/dir2/dir3/dir4/dir5/file1.txt": {
path: "/test_data/dir2/dir3/dir4/dir5/file1.txt",
type: "file",
children: []
},
"/test_data/dir2/dir3/dir4/dir5/file2.txt": {
path: "/test_data/dir2/dir3/dir4/dir5/file2.txt",
type: "file",
children: []
},
"/test_data/new_directory": {
path: "/test_data/new_directory",
type: "folder",
children: [
"/test_data/new_directory/ok.txt",
"/test_data/new_directory/file2.txt"
]
},
"/test_data/new_directory/file2.txt": {
path: "/test_data/new_directory/file2.txt",
type: "file",
children: []
},
"/test_data/new_directory/ok.txt": {
path: "/test_data/new_directory/ok.txt",
type: "file",
children: []
},
"/test_data/reads_1.fq": {
path: "/test_data/reads_1.fq",
type: "file",
children: []
},
"/test_data/test_ref.fa": {
path: "/test_data/test_ref.fa",
type: "file",
children: []
},
"/third_dir": {
path: "/third_dir",
type: "folder",
isRoot: true,
children: ["/third_dir/first.xls"]
},
"/third_dir/first.xls": {
path: "/third_dir/first.xls",
type: "file",
children: []
}
};
This is my attempt, and I have spent way too long on this
const buildChildNodes = (arr, root) => {
let a = []
arr.map((n, idx) => {
a.push('/' + root + '/' + arr[idx])
});
return a;
}
const createTree = paths => {
let finalTree = {};
paths.map(path => {
let tree = {};
let subTree = path.split('/')
subTree.forEach((a,i) => {
tree = {}
let root = subTree[0]
tree.path = '/' + subTree.slice(0, i+1).join('/')
tree.type = subTree.slice(i+1).length > 0 ? 'folder' : 'file'
if(i === 0){
tree.isRoot = true
}
tree.children = buildChildNodes(subTree.slice(i+1), root)
finalTree['/' + subTree.slice(0, i+1).join('/')] = tree
})
})
return finalTree;
};
console.log(JSON.stringify(createTree(paths.sort()), null, 2));
And this is what I end up with, as you can see, the children nodes are not being created correctly:
{
"/other_dir": {
"path": "/other_dir",
"type": "folder",
"isRoot": true,
"children": [
"/other_dir/sub_dir",
"/other_dir/file2.xls"
]
},
"/other_dir/dir2": {
"path": "/other_dir/dir2",
"type": "folder",
"children": [
"/other_dir/newfile.xls"
]
},
"/other_dir/dir2/newfile.xls": {
"path": "/other_dir/dir2/newfile.xls",
"type": "file",
"children": []
},
"/other_dir/sub_dir": {
"path": "/other_dir/sub_dir",
"type": "folder",
"children": [
"/other_dir/file2.xls"
]
},
"/other_dir/sub_dir/file1.xls": {
"path": "/other_dir/sub_dir/file1.xls",
"type": "file",
"children": []
},
"/other_dir/sub_dir/file2.xls": {
"path": "/other_dir/sub_dir/file2.xls",
"type": "file",
"children": []
},
"/test_data": {
"path": "/test_data",
"type": "folder",
"isRoot": true,
"children": [
"/test_data/test_ref.fa"
]
},
"/test_data/dir2": {
"path": "/test_data/dir2",
"type": "folder",
"children": [
"/test_data/dir3",
"/test_data/dir4",
"/test_data/dir5",
"/test_data/file2.txt"
]
},
"/test_data/dir2/dir3": {
"path": "/test_data/dir2/dir3",
"type": "folder",
"children": [
"/test_data/dir4",
"/test_data/dir5",
"/test_data/file2.txt"
]
},
"/test_data/dir2/dir3/dir4": {
"path": "/test_data/dir2/dir3/dir4",
"type": "folder",
"children": [
"/test_data/dir5",
"/test_data/file2.txt"
]
},
"/test_data/dir2/dir3/dir4/dir5": {
"path": "/test_data/dir2/dir3/dir4/dir5",
"type": "folder",
"children": [
"/test_data/file2.txt"
]
},
"/test_data/dir2/dir3/dir4/dir5/file1.txt": {
"path": "/test_data/dir2/dir3/dir4/dir5/file1.txt",
"type": "file",
"children": []
},
"/test_data/dir2/dir3/dir4/dir5/file2.txt": {
"path": "/test_data/dir2/dir3/dir4/dir5/file2.txt",
"type": "file",
"children": []
},
"/test_data/new_directory": {
"path": "/test_data/new_directory",
"type": "folder",
"children": [
"/test_data/ok.txt"
]
},
"/test_data/new_directory/file2.txt": {
"path": "/test_data/new_directory/file2.txt",
"type": "file",
"children": []
},
"/test_data/new_directory/ok.txt": {
"path": "/test_data/new_directory/ok.txt",
"type": "file",
"children": []
},
"/test_data/reads_1.fq": {
"path": "/test_data/reads_1.fq",
"type": "file",
"children": []
},
"/test_data/test_ref.fa": {
"path": "/test_data/test_ref.fa",
"type": "file",
"children": []
},
"/third_dir": {
"path": "/third_dir",
"type": "folder",
"isRoot": true,
"children": [
"/third_dir/first.xls"
]
},
"/third_dir/first.xls": {
"path": "/third_dir/first.xls",
"type": "file",
"children": []
}
}
If anyone could help me out on this I would really appreciate it!
Thank you!
Upvotes: 1
Views: 131
Reputation: 122135
You could use two nested reduce
method where you add path to the children array only if there is next part of the path (which means that the current elements has child) and also if that child is not included already in the array.
const paths = [
"test_data/new_directory/ok.txt",
"test_data/reads_1.fq",
"test_data/test_ref.fa",
"test_data/dir2/dir3/dir4/dir5/file1.txt",
"test_data/dir2/dir3/dir4/dir5/file2.txt",
"test_data/new_directory/file2.txt",
"other_dir/dir2/newfile.xls",
"other_dir/sub_dir/file1.xls",
"other_dir/sub_dir/file2.xls",
"third_dir/first.xls"
];
const result = paths.reduce((acc, e) => {
let prev = ''
e.split('/').reduce((r, path, i, a) => {
prev += '/' + path;
if (!r[prev]) {
const children = []
const type = a[i + 1] ? 'folder' : 'file'
const obj = { path: prev, type, children }
if (i == 0) {
obj.isRoot = true
}
r[prev] = obj
}
if (a[i + 1]) {
const child = prev + '/' + a[i + 1];
if (!r[prev].children.includes(child)) {
r[prev].children.push(child)
}
}
return r
}, acc)
return acc
}, {})
console.log(result)
Upvotes: 0
Reputation: 386868
You could get the sub parts of the strings, check if the path exits or add a new path. Then take the previous path and add the childrens.
const
paths = ["test_data/new_directory/ok.txt", "test_data/reads_1.fq", "test_data/test_ref.fa", "test_data/dir2/dir3/dir4/dir5/file1.txt", "test_data/dir2/dir3/dir4/dir5/file2.txt", "test_data/new_directory/file2.txt", "other_dir/dir2/newfile.xls", "other_dir/sub_dir/file1.xls", "other_dir/sub_dir/file2.xls", "third_dir/first.xls"],
result = paths.sort().reduce((r, p) => {
p.split(/\//).forEach((_, i, p) => {
var path = '/' + p.slice(0, i + 1).join('/');
r[path] = r[path] || { path, type: i + 1 === p.length ? 'file' : 'folder', children: [] };
if (i) {
const prev = '/' + p.slice(0, i).join('/');
if (!r[prev].children.includes(path)) r[prev].children.push(path);
} else {
r[path].isRoot = true;
}
});
return r;
}, {});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }
Upvotes: 2