I have an array of objects that looks like this:
[{ name: 'test',
size: 0,
type: 'directory',
path: '/storage/test' },
{ name: 'asdf',
size: 170,
type: 'directory',
path: '/storage/test/asdf' },
{ name: '2.txt',
size: 0,
type: 'file',
path: '/storage/test/asdf/2.txt' }]
There could be any number of arbitrary path's, this is the result of iterating through files and folders within a directory.
What I'm trying to do is determine the 'root' node of these. Ultimately, this will be stored in mongodb and use materialized path to determine it's relationships.
In this example, /storage/test
is a root with no parent. /storage/test/asdf
has the parent of /storage/test
which is the parent to /storage/test/asdf/2.txt
.
My question is, how would you go about iterating through this array, to determine the parent's and associated children? Any help in the right direction would be great!
Thank you
I have an array of objects that looks like this:
[{ name: 'test',
size: 0,
type: 'directory',
path: '/storage/test' },
{ name: 'asdf',
size: 170,
type: 'directory',
path: '/storage/test/asdf' },
{ name: '2.txt',
size: 0,
type: 'file',
path: '/storage/test/asdf/2.txt' }]
There could be any number of arbitrary path's, this is the result of iterating through files and folders within a directory.
What I'm trying to do is determine the 'root' node of these. Ultimately, this will be stored in mongodb and use materialized path to determine it's relationships.
In this example, /storage/test
is a root with no parent. /storage/test/asdf
has the parent of /storage/test
which is the parent to /storage/test/asdf/2.txt
.
My question is, how would you go about iterating through this array, to determine the parent's and associated children? Any help in the right direction would be great!
Thank you
Share Improve this question asked Oct 23, 2013 at 1:58 dzmdzm 23.5k50 gold badges152 silver badges229 bronze badges 3 |3 Answers
Reset to default 11You can do it like this:
var arr = [] //your array;
var tree = {};
function addnode(obj){
var splitpath = obj.path.replace(/^\/|\/$/g, "").split('/');
var ptr = tree;
for (i=0;i<splitpath.length;i++)
{
node = { name: splitpath[i],
type: 'directory'};
if(i == splitpath.length-1)
{node.size = obj.size;node.type = obj.type;}
ptr[splitpath[i]] = ptr[splitpath[i]]||node;
ptr[splitpath[i]].children=ptr[splitpath[i]].children||{};
ptr=ptr[splitpath[i]].children;
}
}
arr.map(addnode);
console.log(require('util').inspect(tree, {depth:null}));
Output
{ storage:
{ name: 'storage',
type: 'directory',
children:
{ test:
{ name: 'test',
type: 'directory',
size: 0,
children:
{ asdf:
{ name: 'asdf',
type: 'directory',
size: 170,
children: { '2.txt': { name: '2.txt', type: 'file', size: 0, children: {} } } } } } } } }
Assuming /
will never show up in the list of files, something like this should work:
function treeify(files) {
var path = require('path')
files = files.reduce(function(tree, f) {
var dir = path.dirname(f.path)
if (tree[dir]) {
tree[dir].children.push(f)
} else {
tree[dir] = { implied: true, children: [f] }
}
if (tree[f.path]) {
f.children = tree[f.path].children
} else {
f.children = []
}
return (tree[f.path] = f), tree
}, {})
return Object.keys(files).reduce(function(tree, f) {
if (files[f].implied) {
return tree.concat(files[f].children)
}
return tree
}, [])
}
It'll turn the array you mention in the question in to something like this:
[ { name: 'test',
size: 0,
type: 'directory',
path: '/storage/test',
children:
[ { name: 'asdf',
size: 170,
type: 'directory',
path: '/storage/test/asdf',
children:
[ { name: '2.txt',
size: 0,
type: 'file',
path: '/storage/test/asdf/2.txt',
children: [] } ] } ] } ]
I haven't actually tested this with any other data sources, so your milage may vary but at least it ought to nudge you in the right direction.
Solution based on @user568109 but returning results in arrays instead of objects:
function filesToTreeNodes(arr) {
var tree = {}
function addnode(obj) {
var splitpath = obj.fileName.replace(/^\/|\/$/g, "").split('/');
var ptr = tree;
for (let i = 0; i < splitpath.length; i++) {
let node: any = {
fileName: splitpath[i],
isDirectory: true
};
if (i == splitpath.length - 1) {
node.isDirectory = false
}
ptr[splitpath[i]] = ptr[splitpath[i]] || node;
ptr[splitpath[i]].children = ptr[splitpath[i]].children || {};
ptr = ptr[splitpath[i]].children;
}
}
function objectToArr(node) {
Object.keys(node || {}).map((k) => {
if (node[k].children) {
objectToArr(node[k])
}
})
if (node.children) {
node.children = Object.values(node.children)
node.children.forEach(objectToArr)
}
}
arr.map(addnode);
objectToArr(tree)
return Object.values(tree)
}
This is the signature to better understand the input / output formats:
export interface TreeNode {
isDirectory: string
children: TreeNode[]
fileName: string
}
export interface File {
fileName: string
}
export type fileToTreeNodeType = (files: File[]) => TreeNode[]
/storage/test
belongs to the implied node/storage
which in turn would belong to the implied root/
? Not entirely sure what kind of data structure you want to end up with. – Marcus Stade Commented Oct 23, 2013 at 2:16/storage/test
and one at/storage/text/asdf/2.txt
then/storage/text/asdf
would have to be implied unless parent could also mean grandparent. – Marcus Stade Commented Oct 23, 2013 at 2:18