Build a directory tree from an object - javascript

Having an array of items with the following format:
[ { path: '/Folder 1/Folder 1.1',
name: 'Folder 1.1.1',
id: 'Fi6CsP4RWFutOZKsIDoYMSfBPQb-A-lj3C4Jc_zZoG0' },
{ path: '/Folder 1',
name: 'Folder 1.2',
id: 'c2dTN3CgBr9Xik8jdpBkfzR6wZ00oGTX3IbfXrfFujM' },
{ path: '/Folder 1',
name: 'Folder 1.1',
id: 'WmKaOZhzpubcunNNoxbUnqfSYVuNQZDNC852KDJK_G8' },
{ path: '/',
name: 'Folder 1',
id: 'aNRvIvCLyNOgLOmZVzFhoOiZMAz3-p87kBFIGSQS2Yg' },
{ path: '/',
name: 'Folder 2',
id: 'S4FkkQ3hgLTVedIrlSBqe2_1DhrrnLx5szk7-9Wv3X8' } ]
It is possible to take it to a format like the following
var dir = {
"directory": [{
"text": "Folder 1",
"id": 'aNRvIvCLyNOgLOmZVzFhoOiZMAz3-p87kBFIGSQS2Yg',
"nodes": [{
"text": "Folder 1.1",
"id": 'WmKaOZhzpubcunNNoxbUnqfSYVuNQZDNC852KDJK_G8',
"nodes": [{
"text": "Folder 1.1.1",
"id": 'Fi6CsP4RWFutOZKsIDoYMSfBPQb-A-lj3C4Jc_zZoG0'
}]
}, {
"text": "Folder 1.2",
"id": 'c2dTN3CgBr9Xik8jdpBkfzR6wZ00oGTX3IbfXrfFujM'
}]
},
{
"text": "Folder 2",
"id": 'S4FkkQ3hgLTVedIrlSBqe2_1DhrrnLx5szk7-9Wv3X8'
}
]
};
Graphically
Tree
I was thinking using recursion, but I still have not been able to do it.

Here's a non-recursive solution that works by first building a tree, then transforming the tree into the desired structure.
The reason for the first step is due to the slowness of iterating over node arrays in linear time and assumes no order of the flat input array. Performing lookups on an object tree structure speeds up and simplifies the process, making the result tree easy to build in a single traversal.
const treeify = data =>
data.reduce((a, e) => {
let level = a;
e.path.split("/")
.filter(e => e)
.forEach(dir => {
if (!(dir in level)) {
level[dir] = {nodes: {}};
}
level = level[dir].nodes;
})
;
if (e.name in level) {
level[e.name] = {
text: e.name,
id: e.id,
nodes: level[e.name].nodes
};
}
else {
level[e.name] = {text: e.name, id: e.id};
}
return a;
}, {})
;
const format = tree => {
const result = [];
const stack = [[result, tree]];
while (stack.length) {
const [res, curr] = stack.pop();
for (const k in curr) {
const o = {
id: curr[k].id,
text: curr[k].text
};
res.push(o);
if (curr[k].nodes) {
o.nodes = [];
stack.push([o.nodes, curr[k].nodes]);
}
}
}
return {directory: result};
};
const data = [
{
path: '/Folder 1/Folder 1.1',
name: 'Folder 1.1.1',
id: 'Fi6CsP4RWFutOZKsIDoYMSfBPQb-A-lj3C4Jc_zZoG0'
},
{
path: '/Folder 1',
name: 'Folder 1.2',
id: 'c2dTN3CgBr9Xik8jdpBkfzR6wZ00oGTX3IbfXrfFujM'
},
{
path: '/Folder 1',
name: 'Folder 1.1',
id: 'WmKaOZhzpubcunNNoxbUnqfSYVuNQZDNC852KDJK_G8'
},
{
path: '/',
name: 'Folder 1',
id: 'aNRvIvCLyNOgLOmZVzFhoOiZMAz3-p87kBFIGSQS2Yg'
},
{
path: '/',
name: 'Folder 2',
id: 'S4FkkQ3hgLTVedIrlSBqe2_1DhrrnLx5szk7-9Wv3X8'
}
];
console.log(format(treeify(data)));

Related

Mapping array in javacript app in a react app

This is my array, I'm working on react app
const categoryObj = [
{
id: "463e989a-c4f2-4616-85c5-0cb610c5fff0",
name: "Women",
subCategory: [
{
id: "91ba7308-b68e-4d0c-85d8-0cc8272c6bc8",
name: "All",
icon: "AllIcon"
},
{
id: "0e0712c5-0b5a-4d4e-acf5-3d7faf2caa2a",
name: "Clothes",
icon: "ClothesIcon",
sections: [
{
id: "9b7a7a58-04a1-4aba-ba68-0e6b1390021e",
name: "All",
href: "Women/Clothes/All".split(' ').join('-').trim().toLowerCase()
}
]
}
]
}
]
how can I access "women" in this array ? (I have many items like "women" this is just a sample of a large array. if you can suggest a mapping method it's better.)
You can map through it to get the name value in this way:
categoryObj.map((category) => category.name)
const categoryObj = [
{
id: "463e989a-c4f2-4616-85c5-0cb610c5fff0",
name: "Women",
subCategory: [
{
id: "91ba7308-b68e-4d0c-85d8-0cc8272c6bc8",
name: "All",
icon: "AllIcon"
},
{
id: "0e0712c5-0b5a-4d4e-acf5-3d7faf2caa2a",
name: "Clothes",
icon: "ClothesIcon",
sections: [
{
id: "9b7a7a58-04a1-4aba-ba68-0e6b1390021e",
name: "All",
href: "Women/Clothes/All".split(' ').join('-').trim().toLowerCase()
}
]
}
]
},
{
id: "463e989a-c4f2-4616-85c5-0cb610c5fff0",
name: "Men",
subCategory: [
{
id: "91ba7308-b68e-4d0c-85d8-0cc8272c6bc8",
name: "All",
icon: "AllIcon"
},
{
id: "0e0712c5-0b5a-4d4e-acf5-3d7faf2caa2a",
name: "Clothes",
icon: "ClothesIcon",
sections: [
{
id: "9b7a7a58-04a1-4aba-ba68-0e6b1390021e",
name: "All",
href: "Women/Clothes/All".split(' ').join('-').trim().toLowerCase()
}
]
}
]
},
{
id: "463e989a-c4f2-4616-85c5-0cb610c5fff0",
name: "Children",
subCategory: [
{
id: "91ba7308-b68e-4d0c-85d8-0cc8272c6bc8",
name: "All",
icon: "AllIcon"
},
{
id: "0e0712c5-0b5a-4d4e-acf5-3d7faf2caa2a",
name: "Clothes",
icon: "ClothesIcon",
sections: [
{
id: "9b7a7a58-04a1-4aba-ba68-0e6b1390021e",
name: "All",
href: "Women/Clothes/All".split(' ').join('-').trim().toLowerCase()
}
]
}
]
}
]
const result = categoryObj.filter(obj => obj.name === 'Women')
console.log('multi objects :', result)
// if unique with name
const resultUnique = categoryObj.find(obj => obj.name === 'Women')
console.log('unique object :', resultUnique)
This object contains an object and have another variations as sub. If you want to access first name,
categoryObj.map((it) => {
//You can handle it like this
console.log(it.name)
})
But if you want all name of all subCategory,
categoryObj[0].subCategory.map((cat) => {
//You can handle it like this
console.log(cat.name)
})
You have same object-type in and out.
const res = categoryObj.filter(item=>item.name === 'Women');

How to find a node in a tree with JavaScript and get the path used to find or replace that specific object? [duplicate]

This question already has answers here:
How to find a node in a tree with JavaScript
(19 answers)
Closed 1 year ago.
I have and object literal that is essentially a tree that does not have a fixed number of levels. How can I go about searching the tree for a particualy node and then return that node when found in an effcient manner in javascript?
Essentially I have a tree like this and would like to find the node with the title 'randomNode_1' and get back data[0].children[0].children[0] or for a individual search get back .children[0].children[0]
or just a simple way of replacing the object found during the search.
var data = [
{
title: 'topNode',
children: [
{
title: 'node1',
children: [
{
title: 'randomNode_1'
},
{
title: 'node2',
children: [
{
title: 'randomNode_2',
children:[
{
title: 'node2',
children: [
{
title: 'randomNode_3',
}]
}
]
}]
}]
}
]
}];
You can use a recursive generator function...
This example is simple but robust:
var data = [{ title: 'topNode', children: [{ title: 'node1', children: [{ title: 'randomNode_1' }, { title: 'node2', children: [{ title: 'randomNode_2', children: [{ title: 'node2', children: [{ title: 'randomNode_3', }] }] }] }] }] }];
function* findAllNode(child, title) {
for (const node of child) {
if (node.title === title)
yield node;
if (node.children)
yield* findAllNode(node.children, title);
}
}
for (const node of findAllNode(data, "randomNode_1")) {
node.msg = "Hello, World"; // Modify node
break; // Get Only first matched node;
}
// Return All Node
console.log([...findAllNode(data, "randomNode_1")])
This is modified one that also support node index (keys):
var data = [{ title: 'topNode', children: [{ title: 'node1', children: [{ title: 'randomNode_1' }, { title: 'node2', children: [{ title: 'randomNode_2', children: [{ title: 'node2', children: [{ title: 'randomNode_3', }] }] }] }] }] }];
let findByIndex = (data, keys) => keys.reduce((o, k) => o[k], data);
function* findAllNode(child, title) {
for (let i = 0; i < child.length; i++) {
const node = child[i];
if (node.title === title)
yield [node, [i]];
if (node.children) for (const unit of findAllNode(node.children, title)) {
unit[1].unshift(i, "children");
yield unit
}
}
}
for (const [node, index] of findAllNode(data, "randomNode_1")) {
console.log("findByIndex", findByIndex(data, index))
node.msg = "Hello, World"; // Modify node
break; // Get Only first matched node;
}
console.log("Find All Node ", [...findAllNode(data, "randomNode_1")])

Find an object from array of object using an arrray element and replace it

Let say I have two array of objects.
var arr1= [
{
pid: [ '1967', '967' ],
},
{
pid: [ '910', '1967', '967' ],
},
{
pid: [ '967' ],
}
]
var arr2 = [
{ _id: '967', name: 'test pid' },
{ _id: '1967', name: 'test one test' },
{ _id: '910', name: 'this is test name' }
]
is there any way I can find the _id and name from array2 using the id from arr1 and replace element of arr1.pid. like below
arr1 = [
{
pid: [ { _id: '1967', name: 'test one test' }, { _id: '967', name: 'test pid' }],
},
{
pid: [ { _id: '910', name: 'this is test name' }, { _id: '1967', name: 'test one test' }, { _id: '967', name: 'test pid' } ],
},
{
pid: [ { _id: '967', name: 'test pid' } ],
}
]
so far I have done as below
for (var i = 0; i < arr1.length; i++){
var pids = arr1[i].pid
for(var j = 0; j<pids.length; j++){
var result = arr2.filter(obj => {
return obj._id === pids[j]
})
console.log(result) //can not push this into arr1['pid']
}
}
You can map it and inside that you can find the elemenet from second array:
var arr1= [ { pid: [ '1967', '967' ], }, { pid: [ '910', '1967', '967' ], }, { pid: [ '967' ], }];
var arr2 = [ { _id: '967', name: 'test pid' }, { _id: '1967', name: 'test one test' },{ _id: '910', name: 'this is test name' } ];
var result = arr1.map(k=>{
k.pid = k.pid.map(p=>arr2.find(n=>n._id==p));
return k;
});
console.log(result);
Build an object from arr2, so that easy access given key.
Use map on arr1 and update the array.
var arr1 = [
{
pid: ["1967", "967"],
},
{
pid: ["910", "1967", "967"],
},
{
pid: ["967"],
},
];
var arr2 = [
{ _id: "967", name: "test pid" },
{ _id: "1967", name: "test one test" },
{ _id: "910", name: "this is test name" },
];
// Build an object to from array
const all = arr2.reduce(
(acc, curr) => ((acc[curr._id] = { ...curr }), acc),
{}
);
const res = arr1.map(({ pid }) => ({
pid: pid.map((key) => ({ ...all[key] })),
}));
console.log(res);

Nested array filtering, is there a more elegant way?

I'm trying to filter a nested structure, based on a search string.
If the search string is matched in an item, then I want to keep that item in the structure, along with its parents.
If the search string is not found, and the item has no children, it can be discounted.
I've got some code working which uses a recursive array filter to check the children of each item:
const data = {
id: '0.1',
children: [
{
children: [],
id: '1.1'
},
{
id: '1.2',
children: [
{
children: [],
id: '2.1'
},
{
id: '2.2',
children: [
{
id: '3.1',
children: []
},
{
id: '3.2',
children: []
},
{
id: '3.3',
children: []
}
]
},
{
children: [],
id: '2.3'
}
]
}
]
};
const searchString = '3.3';
const filterChildren = (item) => {
if (item.children.length) {
item.children = item.children.filter(filterChildren);
return item.children.length;
}
return item.id.includes(searchString);
};
data.children = data.children.filter(filterChildren);
console.log(data);
/*This outputs:
{
"id": "0.1",
"children": [
{
"id": "1.2",
"children": [
{
"id": "2.2",
"children": [
{
"id": "3.3",
"children": []
}
]
}
]
}
]
}*/
I'm concerned that if my data structure becomes massive, this won't be very efficient.
Can this be achieved in a 'nicer' way, that limits the amount of looping going on? I'm thinking probably using a reducer/transducer or something similarly exciting :)
A nonmutating version with a search for a child.
function find(array, id) {
var child,
result = array.find(o => o.id === id || (child = find(o.children, id)));
return child
? Object.assign({}, result, { children: [child] })
: result;
}
const
data = { id: '0.1', children: [{ children: [], id: '1.1' }, { id: '1.2', children: [{ children: [], id: '2.1' }, { id: '2.2', children: [{ id: '3.1', children: [] }, { id: '3.2', children: [] }, { id: '3.3', children: [] }] }, { children: [], id: '2.3' }] }] },
searchString = '3.3',
result = find([data], searchString);
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

tree search using recursion javascript

I am looking for a way to be able to search in an array, with nested arrays, a node with information. It can be seen as a tree
const data = [
{
id: '1-1',
name: "Factory",
children: [
{
id: '1-1-1',
name: "Areas",
children: [
{
id: '1-1-1-1',
name: "Sales",
children: [
{
id: '1-1-1-1-1',
name: "Bill Gates",
children:[...]
},
...
]
},
...
]
},
...
],
},
...
]
If I wanted to find the node with name: Bill Gates
Try this function, but it does not work properly
const getElements = (treeData, text) => {
return treeData.map(node => {
const textMatch = node.name.toLowerCase().includes(text.toLowerCase());
if (textMatch) {
console.log(node);
return node;
} else {
if (node.children) {
return getElements(node.children, text)
}
}
})
}
In deeper data like Bill Gates Node returns the entire TreeArray, but with all the data that does not contain the name Bill Gates as undefined
You probably don't want to use .map here, because you don't want a mutated array, you just want to find a node. Using a for loop gets the expected result:
const data = [{
id: '1-1',
name: "Factory",
children: [
{
id: '1-1-1',
name: "Areas",
children: [
{
id: '1-1-1-1',
name: "Sales",
children: [
{
id: '1-1-1-1-1',
name: "Bill Gates",
children:[]
},
]
},
]
},
]
}];
const getElements = (treeData, text) => {
for (let i=0, node = treeData[i]; node; i++) {
const textMatch = node.name.toLowerCase().includes(text.toLowerCase());
if (textMatch) {
console.log(node);
return node;
} else if (node.children) {
return getElements(node.children, text)
}
}
};
getElements(data, 'Bill Gates');

Categories

Resources