Return children tree from array - javascript

I have an array of regions the highest region has key: 10 and parent_id: null and I want to restructure this array to return a tree.
Regions tree should look like if the input is [10]
Egypt
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Array:
[
{
"key": 1,
"title": "Zone 1",
"parent_id": 10
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10
},
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1
},
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2
},
{
"key": 5,
"title": "Delta",
"parent_id": 2
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2
},
{
"key": 7,
"title": "Giza",
"parent_id": 1
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2
},
{
"key": 10,
"title": "Egypt",
"parent_id": null
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1
},
{
"key": 12,
"title": "test",
"parent_id": 4
}
]
The output I want to achieve if input is [10]:
[
{
"key": 10,
"title": "Egypt",
"parent_id": null,
"children": [
{
"key": 1,
"title": "Zone 1",
"parent_id": 10,
"children": [
{
"key": 3,
"title": "Tagamo3",
"parent_id": 1,
"children": []
},
{
"key": 7,
"title": "Giza",
"parent_id": 1,
"children": []
},
{
"key": 8,
"title": "Helwan",
"parent_id": 1,
"children": []
},
{
"key": 11,
"title": "Fayoum",
"parent_id": 1,
"children": []
}
]
},
{
"key": 2,
"title": "Zone 2",
"parent_id": 10,
"children": [
{
"key": 4,
"title": "Gesr ElSuis",
"parent_id": 2,
"children": [
{
"key": 12,
"title": "test",
"parent_id": 4,
"children": []
}
]
},
{
"key": 5,
"title": "Delta",
"parent_id": 2,
"children": []
},
{
"key": 6,
"title": "Mohandeseen",
"parent_id": 2,
"children": []
},
{
"key": 9,
"title": "Down Town",
"parent_id": 2,
"children": []
}
]
}
]
}
]
Regions tree should look like if the input is [1,2]
Zone 1
Tagamo3
Giza
Helwan
Fayoum
Zone 2
Gesr ElSuis
test
Delta
Mohandeseen
Down Town
Regions tree should look like if the input is [1]
Zone 1
Tagamo3
Giza
Helwan
Fayoum

May not be the most optimized, but I gave it a try:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(x => ({ ...x, children: buildTree(x.key) }));
console.log(buildTree(null));
To build multiple trees, this could work:
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
const buildNode = x => ({...x, children: buildTree(x.key)});
const buildTree = key => arr.filter(x => x.parent_id === key)
.map(buildNode);
const buildTrees = keys => arr.filter(x => keys.includes(x.key))
.map(buildNode);
console.log(buildTrees([1, 2]));

based on jcalz snippet, but requires only a single pass over the input array.
const arr = [{key:1,title:"Zone 1",parent_id:10},{key:2,title:"Zone 2",parent_id:10},{key:3,title:"Tagamo3",parent_id:1},{key:4,title:"Gesr ElSuis",parent_id:2},{key:5,title:"Delta",parent_id:2},{key:6,title:"Mohandeseen",parent_id:2},{key:7,title:"Giza",parent_id:1},{key:8,title:"Helwan",parent_id:1},{key:9,title:"Down Town",parent_id:2},{key:10,title:"Egypt",parent_id:null},{key:11,title:"Fayoum",parent_id:1},{key:12,title:"test",parent_id:4}];
/*const lookup: Record<number|"roots", Tree[]> = { roots: [] };*/
const lookup = { roots: [] };
for (const item of arr) {
// get or create the "children" array for my parent.
// parent may not be known yet, but I'm already collecting its children, my siblings.
const siblings = lookup[item.parent_id ?? "roots"] ??= [];
// add myself to that children array.
siblings.push({
...item,
// get or create my children array.
children: lookup[item.key] ??= []
});
}
// it's up to you to handle `lookup.roots.length !== 1`
console.log(lookup.roots);
.as-console-wrapper{top:0;max-height:100%!important}

Related

How to get desired category tree with top parent having predefined value

Hi i want to get children tree from a category list.
here is my input data
[
{ "parent_id": -1, "name": "Toothpaste", "id": 99 },
{
"parent_id": -1,
"name": "Cake",
"id": 3
},
{
"parent_id": 3,
"name": "Chocolate Cake",
"id": 4
},
{
"parent_id": 3,
"name": "Walnut Cake",
"id": 5
},
{
"parent_id": 4,
"name": "Chocolate Cake mixin 1",
"id": 6
}
]
my desired output will look like below one
[ { "parent_id": -1, "name": "Toothpaste", "id": 99 },
{
"parent_id": -1,
"name": "Cake",
"id": 3,
"children":[
{
"parent_id": 3,
"name": "Chocolate Cake",
"id": 4,
"children":[ //<--- observe this one not there in my output
{
"parent_id": 4,
"name": "Chocolate Cake mixin 1",
"id": 6
}
]
},
{
"parent_id": 3,
"name": "Walnut Cake",
"id": 5
}
],
}
]
The problem i'm facing is that i'm unable to push data into 2nd level i,e chocolate cake is having children but i'm unable to push that into chocolate cake children
Note: solution must work for any level of nesting
here is what i have tried
function getChildrenTree(childList){
let childMap = {};
for(let i = 0; i < childList.length; i++){
if(childList[i].parent_id === -1)
childMap[childList[i].id] = {name:childList[i].name,children:[]};
}
for(let i = 0; i < childList.length; i++){
if(childMap && childMap.hasOwnProperty(childList[i].parent_id) && childMap[childList[i].parent_id].hasOwnProperty('children')) childMap[childList[i].parent_id].children.push(childList[i]);
}
return Object.values(childMap);
}
getChildrenTree([ { "parent_id": -1, "name": "Toothpaste", "id": 99 },{ "parent_id": -1, "name": "Cake", "id": 3 }, { "parent_id": 3, "name": "Chocolate Cake", "id": 4 }, { "parent_id": 3, "name": "Walnut Cake", "id": 5 }, { "parent_id": 4, "name": "Chocolate Cake mixin 1", "id": 6 } ])
Here's an approach you can try.
const flat = [
{ "parent_id": -1, "name": "Toothpaste", "id": 99 },
{
"parent_id": -1,
"name": "Cake",
"id": 3
},
{
"parent_id": 3,
"name": "Chocolate Cake",
"id": 4
},
{
"parent_id": 3,
"name": "Walnut Cake",
"id": 5
},
{
"parent_id": 4,
"name": "Chocolate Cake mixin 1",
"id": 6
}
];
const makeTree = (arr, parent) => {
const branch = arr.filter(node => node.parent_id === parent);
branch.forEach(node => {
const children = makeTree(arr, node.id);
if (children.length) {
node.children = children;
}
});
return branch;
};
const tree = makeTree(flat, -1);
console.log(tree);
You could take a single loop and and object for collecting the nodes and return a tree by taking all children from the root node.
const
buildTree = (data, root) => {
var t = {};
data.forEach(o => {
Object.assign(t[o.id] = t[o.id] || {}, o);
((t[o.parent_id] ??= {}).children ??= []).push(t[o.id]);
});
return t[root].children
},
data = [{ parent_id: -1, name: "Toothpaste", id: 99 }, { parent_id: -1, name: "Cake", id: 3 }, { parent_id: 3, name: "Chocolate Cake", id: 4 }, { parent_id: 3, name: "Walnut Cake", id: 5 }, { parent_id: 4, name: "Chocolate Cake mixin 1", id: 6 }],
tree = buildTree(data, -1);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Array object grouping in JavaScript

I need to convert below unformatted JSON format into formatted input. We need to find id's similar to parent id for different items inside array element of object and then need to push it into children to that id. Below is my code that needs to transform
Input
{
"0": [
{
"id": 10,
"title": "House",
"level": 0,
"children": [],
"parent_id": null
}
],
"1": [
{
"id": 12,
"title": "Red Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
}
],
"2": [
{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
},
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
}
]
}
Output
[
{
"id": 10,
"title": "House",
"level": 0,
"children": [
{
"id": 12,
"title": "RedRoofff",
"level": 1,
"children": [
{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
}
],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 13,
"title": "Wall",
"level": 1,
"children": [
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
}
],
"parent_id": 10
}
],
"parent_id": null
}
]
Please find the solution to above problem.
first, we track the node with Id and then we update the children array like this.
(btw, your input have a missing node, 13)
const input = {
"0": [{
"id": 10,
"title": "House",
"level": 0,
"children": [],
"parent_id": null
}, {
"id": 13,
"title": "Wall",
"level": 0,
"children": [],
"parent_id": null
}],
"1": [{
"id": 12,
"title": "Red Roof",
"level": 1,
"children": [],
"parent_id": 10
},
{
"id": 18,
"title": "Blue Roof",
"level": 1,
"children": [],
"parent_id": 10
},
],
"2": [{
"id": 17,
"title": "Blue Windoww",
"level": 2,
"children": [],
"parent_id": 12
},
{
"id": 16,
"title": "Door",
"level": 2,
"children": [],
"parent_id": 13
},
]
};
const results = [];
const mapId2Node = Object.values(input).reduce((acc, vals) => {
vals.forEach(val => {
acc[val.id] = val;
if (val.parent_id === null) {
results.push(val);
}
});
return acc;
}, {});
Object.values(input).forEach(vals => {
vals.forEach(val => {
if (val.parent_id !== null) {
mapId2Node[val.parent_id].children.push(val);
}
});
});
conosle.log(results);

Find empty arrays in nested array and remove them in Javascript

I have a nested and hierarchical array of objects, which looks like this
[{
"id": 0,
"name": "E00-E90 Stoffwechselstörungen",
"parentId": null,
"children": [{
"id": 1,
"name": "E70-E90 Stoffwechselstörungen",
"parentId": 0,
"children": [{
"id": 2,
"name": "E70.- Störungen des Stoffwechsels aromatischer Aminosäuren",
"parentId": 1,
"children": []
}, {
"id": 3,
"name": "E71.- Störungen des Stoffwechsels verzweigter Aminosäuren und des Fettsäurestoffwechsels",
"parentId": 1,
"children": []
}, {
"id": 4,
"name": "E72.- Sonstige Störungen des Aminosäurestoffwechsels",
"parentId": 1,
"children": []
},
...
Now I want to remove the empty array "children": [] from the last children.
I've tried it with reduce but it doesn't work without any error.
var lastElementLength = list.length - 1
const findItemNested = (arr, itemId, nestingKey) => (
arr.reduce((a, item) => {
if (a) return a;
if (item.id === itemId) return item;
if (item[nestingKey]) return findItemNested(item[nestingKey], itemId, nestingKey)
}, null)
);
const resEmptyChildren = findItemNested(roots, lastElementLength, "children");
console.log('resEmptyChildren', resEmptyChildren)
You could use recursion for that.
var tInput = [{
"id": 0,
"name": "E00-E90 Stoffwechselstörungen",
"parentId": null,
"children": [{
"id": 1,
"name": "E70-E90 Stoffwechselstörungen",
"parentId": 0,
"children": [{
"id": 2,
"name": "E70.- Störungen des Stoffwechsels aromatischer Aminosäuren",
"parentId": 1,
"children": []
}, {
"id": 3,
"name": "E71.- Störungen des Stoffwechsels verzweigter Aminosäuren und des Fettsäurestoffwechsels",
"parentId": 1,
"children": []
}, {
"id": 4,
"name": "E72.- Sonstige Störungen des Aminosäurestoffwechsels",
"parentId": 1,
"children": []
}]
}]
}];
(function removeEmptyChildrenProperties(input){
console.log('Checking id:', input.id);
if(input.hasOwnProperty('children')){
if(input.children && input.children.length){
input.children.forEach(removeEmptyChildrenProperties)
}
else{
console.log('Remove children on id:', input.id);
delete input.children
}
};
return input
}).apply(null, tInput);
console.log(JSON.stringify(tInput));

Group elements of array by sub string JavaScript

I have a list of elements like this
[{
"id": 1,
"id_team": "1.",
"name": "Name",
}, {
"id": 2,
"id_team": "2.",
"name": "Name",
}, {
"id": 3,
"id_team": "3.",
"name": "Name",
}, {
"id": 4,
"id_team": "4.",
"name": "Name",
}, {
"id": 5,
"id_team": "5.",
"name": "Name",
}, {
"id": 6,
"id_team": "2.1.",
"name": "Name",
}, {
"id": 7,
"id_team": "6.",
"name": "Name",
}, {
"id": 8,
"id_team": "1.1.",
"name": "Name",
}, {
"id": 9,
"id_team": "1.2.",
"name": "Name",
}, {
"id": 10,
"id_team": "1.3.",
"name": "Name",
}, {
"id": 11,
"id_team": "1.4.",
"name": "Name",
}]
a shown in the example, i have some string in the column id_team that are inside other string in the same column in the array
For example, the main team would be 1. then 1.1. and 1.2. and so on would be part of 1.
so the result must be like this
[{
"id": 1,
"id_team": "1.",
"name": "Name",
"subteams": [{
"id": 8,
"id_team": "1.1.",
"name": "Name",
}, {
"id": 9,
"id_team": "1.2.",
"name": "Name",
}, {
"id": 10,
"id_team": "1.3.",
"name": "Name",
}, {
"id": 11,
"id_team": "1.4.",
"name": "Name",
},],
}, {
"id": 2,
"id_team": "2.",
"name": "Name",
"subitems": [{
"id": 6,
"id_team": "2.1.",
"name": "Name",
},]
}, {
"id": 3,
"id_team": "3.",
"name": "Name",
"subitems": [],
}, {
"id": 4,
"id_team": "4.",
"name": "Name",
"subitems": [],
}, {
"id": 5,
"id_team": "5.",
"name": "Name",
"subitems": [],
}, {
"id": 7,
"id_team": "6.",
"name": "Name",
"subitems": [],
},]
it is doable? i use lodash if it would make it easier.
EDIT
I have something like this
teams.filter(item => item.id_team.length <= size).map((item) => {
const subteams = teams.filter((team) =>
team.id_team.indexOf(item.id_team) === 0 && item.id_team !== team.id_team);
console.log(subteams);
return {
...item,
subteams,
};
}) || [];
this kinda work, as it will insert in the sub teams array, but the problem is that this must work for many levels, take this example
[{
"id": 1,
"id_team": "1.",
"name": "Team Name",
"subteams": [{
"id": 8,
"id_team": "1.1.",
"name": "Team Name",
}, {
"id": 9,
"id_team": "1.2.",
"name": "Team Name",
}, {
"id": 10,
"id_team": "1.3.",
"name": "Team Name",
}, {
"id": 11,
"id_team": "1.4.",
"name": "Team Name",
}, {
"id": 12,
"id_team": "1.1.1",
"name": "Team Name",
}]
}, {
"id": 2,
"id_team": "2.",
"name": "Team Name",
"subteams": [{
"id": 6,
"id_team": "2.1.",
"name": "Team Name",
}]
}, {
"id": 3,
"id_team": "3.",
"name": "Team Name",
"subteams": []
}, {
"id": 4,
"id_team": "4.",
"name": "Team Name",
"subteams": []
}, {
"id": 5,
"id_team": "5.",
"name": "Team Name",
"subteams": []
}, {
"id": 7,
"id_team": "6.",
"name": "Team Name",
"subteams": []
}]
in this example there is a team with id_team = 1.1.1., how can i add that team to a sub team of the team with id_team 1.1. with the same logic, i tried to use the same code recursively but it didnt work.
const items = data.reduce((acc, item, index) => {
const id_team = item.id_team.split('.')[0] + '.';
const current = acc.find(record => record.id_team === id_team);
if(current) current.subteams.push(item);
else acc.push({
id: index + 1,
id_team,
subteams: [item]
});
return acc;
}, []);
let data = [{
"id": 1,
"id_team": "1.",
"name": "Name",
}, {
"id": 2,
"id_team": "2.",
"name": "Name",
}, {
"id": 3,
"id_team": "3.",
"name": "Name",
}, {
"id": 4,
"id_team": "4.",
"name": "Name",
}, {
"id": 5,
"id_team": "1.2.5.",
"name": "Name",
}, {
"id": 6,
"id_team": "2.1.",
"name": "Name",
}, {
"id": 7,
"id_team": "6.",
"name": "Name",
}, {
"id": 8,
"id_team": "1.1.",
"name": "Name",
}, {
"id": 9,
"id_team": "1.2.",
"name": "Name",
}, {
"id": 10,
"id_team": "1.3.",
"name": "Name",
}, {
"id": 11,
"id_team": "1.4.",
"name": "Name",
}];
data = data.map(node=>{
let t = node.id_team.split('.').filter(n=>n);
let parent = t.splice(0,t.length-1).join('.');
return {
...node,
parent: !!parent ? parent+'.' : null
}
});
let tree = (items, id = null) =>
items
.filter(item => item.parent === id)
.map(item => ({ ...item, subTeams: tree(items, item.id_team) }));
console.log(JSON.stringify(tree(data),null,2));
From the code of the example, i just checked if the subteams array has data, if it does, i use an auxiliar with the recursive function and assigned it to the subteams.
getSubTeams(teams, size) {
const filtered = teams.filter(item => item.id_team.length <= size);
return filtered.map((item) => {
let subteams = teams.filter((team) => team.id_team.indexOf(item.id_team) === 0 && item.id_team !== team.id_team);
if (subteams.length > 0) {
let aux = this.getSubTeams(subteams, size + 2);
subteams = [
...aux,
];
}
return {
...item,
subteams,
};
}) || [];
},

iterate over heavily nested javascript object to find data

I have below JavaScript object pattern.
{
"_id": 2,
"children": [
{
"_id": 3,
"children": [
{
"_id": 5,
"children": [
{
"_id": 9,
"children": [],
"id_category": 9,
"parent_id": 5
},
{
"_id": 10,
"children": [],
"id_category": 10,
"parent_id": 5
}],
"id_category": 5,
"parent_id": 3
},
{
"_id": 6,
"children": [],
"id_category": 6,
"parent_id": 3
}],
"id_category": 3,
"parent_id": 2
},
{
"_id": 4,
"children": [
{
"_id": 7,
"children": [],
"id_category": 7,
"parent_id": 4
},
{
"_id": 8,
"children": [],
"id_category": 8,
"parent_id": 4
}],
"id_category": 4,
"parent_id": 2
}],
"id_category": 2,
"parent_id": 1
}
Now I want to get whole data for which I pass the value of _id.
For example if I pass 5, it will return the whole object of 5
{
"_id": 5,
"children": [
{
"_id": 9,
"children": [],
"id_category": 9,
"parent_id": 5
},
{
"_id": 10,
"children": [],
"id_category": 10,
"parent_id": 5
}],
"id_category": 5,
"parent_id": 3
}
The problem is Level of nesting is unlimited .
Please suggest.
Use a recursive function.
Something like this (untested, but should be ok)
function search(value, tree) {
if (tree._id === value)
return value;
var numChildren = tree.children.length;
for (var i = 0; i < numChildren; ++i) {
var result = search(value, tree.children[i]);
if (result)
return result;
}
return null;
}
var tree = {...your big json hash...};
var found = search(5, tree);
console.log(found);

Categories