Link child and parent on the basic of depth level - javascript

I have all my parent children in a single array data.What i want is to add a new attribute (level) on each objects.
Given i have data as
var data = [
{
id: 1,
parent_id: 0,
name: "Child1",
},
{
id: 4,
parent_id: 1,
name: "Child11",
},
{
id: 5,
parent_id: 4,
name: "Child111",
},
{
id: 11,
parent_id: 4,
name: "Child112"
},
{
id: 13,
parent_id: 11,
name: "Child1121",
},
{
id: 21,
parent_id: 11,
name: "Child1122"
},
{
id: 22,
parent_id: 11,
name: "Child1123"
},
{
id: 24,
parent_id: 1,
name: 'Child12'
}
]
I want a child-parent relationship based on the parent_id of the children and assign a new attribute in each object of the array as level which represents the depth level of the children based on its parent.My Expected result is :
var data = [
{
id: 1,
parent_id: 0, <-------represents root
name: "Child1",
level:0 <--------level based on its parent_id
},
{
id: 4,
parent_id: 1
name: "Child11",
level:1
},
{
id: 5,
parent_id: 4,
name: "Child111",
level:2
},
{
id: 11,
parent_id: 4,
name: "Child112",
level:2
},
{
id: 13,
parent_id: 11,
name: "Child1121",
level:3
},
{
id: 21,
parent_id: 11,
name: "Child1122",
level:3
},
{
id: 22,
parent_id: 11,
name: "Child1123",
level:3
},
{
id: 24,
parent_id: 1,
name: 'Child12',
level:1
}
]
My Code
function buildTree(elements, parent_id, level = 0) {
elements.forEach(element => {
if (element['parent_id'] == parent_id) {
console.log('parent_id', parent_id);
// elements.filter(item=>item!==element);
element['level'] = level;
}
else{
buildTree(elements,parent_id,level+1);
}
})
return elements;
}

For sorted data, you could take an object for the level count and map a new data set.
var data = [{ id: 1, parent_id: 0, name: "Child1" }, { id: 4, parent_id: 1, name: "Child11" }, { id: 5, parent_id: 4, name: "Child111" }, { id: 11, parent_id: 4, name: "Child112" }, { id: 13, parent_id: 11, name: "Child1121" }, { id: 21, parent_id: 11, name: "Child1122" }, { id: 22, parent_id: 11, name: "Child1123" }, { id: 24, parent_id: 1, name: 'Child12' }],
levels = {},
result = data.map(o => ({
...o,
level: levels[o.id] = o.parent_id in levels
? levels[o.parent_id] + 1
: 0
}));
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Try this
let parentLevel = []
data.map(parent => {
const { parent_id } = parent
if (!parentLevel.includes(parent_id)) {
parentLevel.push(parent_id);
}
})
const updatedData = data.map(parent => {
const { parent_id } = parent
parent.level = parentLevel.indexOf(parent_id)
return parent
})
console.log(updatedData);
The result is
(8) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {id: 1, parent_id: 0, name: "Child1", level: 0}
1: {id: 4, parent_id: 1, name: "Child11", level: 1}
2: {id: 5, parent_id: 4, name: "Child111", level: 2}
3: {id: 11, parent_id: 4, name: "Child112", level: 2}
4: {id: 13, parent_id: 11, name: "Child1121", level: 3}
5: {id: 21, parent_id: 11, name: "Child1122", level: 3}
6: {id: 22, parent_id: 11, name: "Child1123", level: 3}
7: {id: 24, parent_id: 1, name: "Child12", level: 1}

If data is not sorted in a way that the parent is guaranteed to come before any of its children, then use a Map keyed by id values, which also gives better efficiency (no linear lookup in every iteration):
let data = [{ id: 1, parent_id: 0, name: "Child1" }, { id: 4, parent_id: 1, name: "Child11" }, { id: 5, parent_id: 4, name: "Child111" }, { id: 11, parent_id: 4, name: "Child112" }, { id: 13, parent_id: 11, name: "Child1121" }, { id: 21, parent_id: 11, name: "Child1122" }, { id: 22, parent_id: 11, name: "Child1123" }, { id: 24, parent_id: 1, name: 'Child12' }];
// optional step if you don't want to mutate the original objects in the array:
data = data.map(o => ({...o}));
const map = new Map(data.map(o => [o.id, o])).set(0, { level: -1 });
const setLevel = o => "level" in o ? o.level : (o.level = 1 + setLevel(map.get(o.parent_id)));
data.forEach(setLevel);
console.log(data);
You can omit the optional assignment when you are OK with adding the level property to the existing objects. But if you want the original data objects to remain untouched, and have newly created objects for storing the level property, then keep that line in.

Related

How can I convert a plain array into an array with objects?

in my vuejs project I'm working on, the information from the api comes in the form of a flat array. I need to edit this incoming data and convert it to the following format.
For example, I need to arrange the parent id of an object to be the id of its parent item.
i tried this way but the tree is returning as empty
convertToTree(data) {
const tree = [];
const mapped = {};
for (let i = 0; i < data.length; i += 1) {
mapped[data[i].id] = data[i];
mapped[data[i].id].children = [];
}
for (let i = 0; i < data.length; i += 1) {
if (mapped[data[i].parentId]) {
mapped[data[i].parentId].children.push(mapped[data[i]]);
} else {
tree.push(mapped[data[i]]);
}
}
return tree;
},
How can I solve this problem, I am waiting for your ideas.
data from api
{
"id": 1,
"parentId": 0,
}, {
"id": 2,
"parentId": 0,
}, {
"id": 3,
"parentId": 0,
}, {
"id": 4,
"parentId": 3,
}, {
"id": 5,
"parentId": 3,
}, {
"id": 6,
"parentId": 4,
}, {
"id": 7,
"parentId": 4,
}, {
"id": 8,
"parentId": 5,
}, {
"id": 9,
"parentId": 5,
}, {
"id": 10,
"parentId": 0,
}
this is how i want to edit data
items: [{
id: 1,
parentId: 0,
children: [{
id: 10,
parentId: 1,
}, ],
id: 2,
parentId: 0,
children: [],
id: 3,
parentId: 0,
children: [{
id: 4,
parentId: 3,
children: [{
id: 6,
parentId: 4,
children: [],
},
{
id: 7,
parentId: 4,
children: [],
},
{
id: 8,
parentId: 4,
children: [],
},
],
},
{
id: 5,
parentId: 3,
children: [{
id: 9,
parentId: 5,
children: [],
},
{
id: 10,
parentId: 5,
children: [],
},
{
id: 11,
parentId: 5,
children: [],
},
],
},
],
}, ],
You can try to directly loop through the dataArray and push the child object into parentObj.children, then filter the dataArray to get all the root nodes of trees (Tree structures).
for (obj of dataArr) {
if (obj.parentId) {
let parent = dataArr.find((i) => i.id === obj.parentId);
if (parent) {
parent.children = [...parent.children || [], obj]
// ↑ this creates an array if parent.children is undefined, avoiding error
}
}
}
By now, the dataArray becomes something like this:
0: {id: 1, parentId: 0}
1: {id: 2, parentId: 0}
2: {id: 3, parentId: 0, children: Array(2)}
3: {id: 4, parentId: 3, children: Array(2)}
4: {id: 5, parentId: 3, children: Array(2)}
5: {id: 6, parentId: 4}
6: {id: 7, parentId: 4}
7: {id: 8, parentId: 5}
8: {id: 9, parentId: 5}
9: {id: 10, parentId: 0}
Then, filter the dataArray by removing objects that got no parent.
let newData = dataArr.filter((obj) => !obj.parentId);
The result is an array with objects that are root nodes of tree structures.
0: {id: 1, parentId: 0}
1: {id: 2, parentId: 0}
2: {id: 3, parentId: 0, children: Array(2)}
3: {id: 10, parentId: 0}
newData[2]:
2:
children: Array(2)
0:
children: Array(2)
0: {id: 6, parentId: 4}
1: {id: 7, parentId: 4}
id: 4
parentId: 3
1:
children: Array(2)
0: {id: 8, parentId: 5}
1: {id: 9, parentId: 5}
id: 5
parentId: 3
id: 3
parentId: 0

Filtering an object based on an parent key in the object (Javascript)

Unfiltered Object
I have this JSON object:
items = [
{id: 1, name: "home", parent: 0, active: 1, order: 0},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
{id: 3, name: "report1", parent: 2, active: 1, order: 11},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2},
{id: 5, name: "report2", parent: 2, active: 1, order: 21},
{id: 6, name: "report3", parent: 2, active: 1, order: 22},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23}
]
Object I want
I want to filter it by parent, keeping any with a parent number of 0, and moving any values with a parent number that matches the id into its children. So I want something like this:
itemsUpdated= [
{ id: 1,
name: "home",
parent: 0,
active: 1,
order: 0,
children:[]
},
{ id: 2,
name: "dashboard",
parent: 0,
active: 1,
order: 1,
children:[
{id: 3, name: "report1", parent: 2, active: 1, order: 11, children: []},
{id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
{id: 6, name: "report3", parent: 2, active: 1, order: 22, children:[]}
]
},
{ id: 4,
name: "analytics",
parent: 0,
active: 1,
order: 2,
children:[
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23, children:[]}
]
}
]
My Approach
so far I have managed to add a children key with an empty array to every item:
let itemsUpdated = items;
for(let i = 0 ; i < itemsUpdated .length; i++){
itemsUpdated [i].children = [];
}
//MY UPDATED ITEMS LOOKS LIKE THIS
updatedItems = [
{id: 1, name: "home", parent: 0, active: 1, order: 0, children:[]},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1, children:[]},
{id: 3, name: "report1", parent: 2, active: 1, order: 11, children:[]},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2, children:[]},
{id: 5, name: "report2", parent: 2, active: 1, order: 21, children:[]},
{id: 6, name: "report3", parent: 2, active: 1, order: 22, children:[]},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23, children:[]}
]
How would I go about filtering and reducing this array down ?
This is a case for reduce(), accumulating each object into the children array of its parent indexed by id in the accumulator object. The result is the array stored in the ['0'] property of the returned object.
The advantage of this over some of the other approaches is that it doesn't employ nested loops.
(items array edited from question to include nested children: id: 7 is a child of id: 6)
const items = [
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]
const result = items.reduce((a, o) => {
a[o.id] = a[o.id] || [];
a[o.parent] = a[o.parent] || [];
a[o.parent].push({ ...o, children: a[o.id] });
return a;
}, {})['0'];
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
A little more concise using logical nullish assignment (??=)
const items = [
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 4, name: "analytics", parent: 0, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 6, active: 1, order: 23 }
]
const result = items
.reduce((a, o) => (
(a[o.parent] ??= []).push({ ...o, children: (a[o.id] ??= []) }), a), {}
)['0'];
console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
ik, ik, im the latest to answer but this was a really fun question :D
let items = [
{id: 1, name: "home", parent: 0, active: 1, order: 0},
{id: 2, name: "dashboard", parent: 0, active: 1, order: 1},
{id: 3, name: "report1", parent: 2, active: 1, order: 11},
{id: 4, name: "analytics", parent: 0, active: 1, order: 2},
{id: 5, name: "report2", parent: 2, active: 1, order: 21},
{id: 6, name: "report3", parent: 2, active: 1, order: 22},
{id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23}
]
//I assume you wont want the original items(since with reference logic.. some editing to this will be done)
let itemsUpdated = items //turn this line into 'let itemsUpdated = JSON.parse(JSON.stringify(items))' if you don't want items edited
//firstly a finder function to return elements which parents match an id n
function findMatches(n){
let arr=[]
itemsUpdated.forEach(a=>{if(a.parent==n){arr.push(a)}})
return arr
}
//now to link >:D
itemsUpdated.forEach(a=>{
a.children=[] //your for loop's equivalent :D
let matches=findMatches(a.id)
if(matches.length){
matches.forEach(b=>{a.children.push(b)})
}
})
//now to filter as the finishing touch
itemsUpdated=itemsUpdated.filter(a=>a.parent==0)
console.log(itemsUpdated)
const items = [
{ id: 1, name: 'home', parent: 0, active: 1, order: 0 },
{ id: 2, name: 'dashboard', parent: 0, active: 1, order: 1 },
{ id: 3, name: 'report1', parent: 2, active: 1, order: 11 },
{ id: 4, name: 'analytics', parent: 0, active: 1, order: 2 },
{ id: 5, name: 'report2', parent: 2, active: 1, order: 21 },
{ id: 6, name: 'report3', parent: 2, active: 1, order: 22 },
{ id: 7, name: 'analytics_page1', parent: 4, active: 1, order: 23 }
];
const itemsUpdated = items
.filter(el => !el.parent)
.map((el, idx) => {
el.children = [];
items.forEach(e => {
if (e.parent === idx+1) {
el.children.push(e);
}
});
return el;
});
console.log(itemsUpdated);
I also want to submit my trial with recursion in mind for nested children, you can see the example of id: 4
let items = [
{ id: 1, name: "home", parent: 0, active: 1, order: 0 },
{ id: 2, name: "dashboard", parent: 0, active: 1, order: 1 },
{ id: 3, name: "report1", parent: 2, active: 1, order: 11 },
{ id: 4, name: "analytics", parent: 3, active: 1, order: 2 },
{ id: 5, name: "report2", parent: 2, active: 1, order: 21 },
{ id: 6, name: "report3", parent: 2, active: 1, order: 22 },
{ id: 7, name: "analytics_page1", parent: 4, active: 1, order: 23 }
];
//adds children empty array
items.forEach((item) => {
item.children = [];
return item;
});
//filters parents on 0 level first
let newItems = items.filter((item) => item.parent === 0);
//recursion
items.forEach((item) => searchParent(newItems, item));
console.log(newItems);
function searchParent(someArray, childObject) {
someArray.forEach((parent) => {
if (parent.id === childObject.parent) {
parent.children.push(childObject);
} else {
if (parent.children) searchParent(parent.children, childObject);
}
});
}

How to generate a nested array of objects from an array of objects in nested set model?

I'm trying to create a function that can convert a nested set model array of objects to a normal nested array of objects within array of objects.
Currently, I'm not satisfied with my temporary solution that is limited to a depth of 2. Basically it is server-side controller using some Knex:
const getCategories = (res, db) => {
db.raw(`
SELECT child.id, child.name, child.path
FROM product_category parent
JOIN product_category child
ON child.lower BETWEEN parent.lower AND parent.upper
WHERE parent.id = 1
AND
(
SELECT COUNT(*)
FROM product_category node
WHERE child.lower BETWEEN node.lower AND node.upper
AND node.lower BETWEEN parent.lower AND parent.upper
) = 2
ORDER BY child.id
`)
.then(categories => {
if (categories.rows.length) {
const categoryPromises = categories.rows.map(category => {
return db.raw(`
SELECT child.id, child.name, child.path
FROM product_category parent
JOIN product_category child
ON child.lower BETWEEN parent.lower AND parent.upper
WHERE parent.id = ${category.id}
AND
(
SELECT COUNT(*)
FROM product_category node
WHERE child.lower BETWEEN node.lower AND node.upper
AND node.lower BETWEEN parent.lower AND parent.upper
) = 2
`)
.then(subcategories => {
return { ...category, subcategories: subcategories.rows }
})
})
return Promise.all(categoryPromises)
.then(products => {
res.json(products)
})
} else {
res.status(400).json("No categories")
}
})
}
The schema is here: http://sqlfiddle.com/#!17/a20af
Nested set model is a way of handling hierarchical data that encloses the children node into boundaries (commonly called lft and rgt). So if the lft and rgt values of node1 are between the lft and rgt value of node2, it means that node1 is a children of node2 (I hope that cleared things up).
For example, I have the following array:
const categories = [
{
id: 1,
name: "Products",
lft: 1,
rgt: 22
},
{
id: 2,
name: "Boats",
lft: 2,
rgt: 15
},
{
id: 3,
name: "Rescue Boats",
lft: 3,
rgt: 4
},
{
id: 4,
name: "Dive Boats",
lft: 5,
rgt: 6
},
{
id: 5,
name: "Tamarans",
lft: 7,
rgt: 8
},
{
id: 6,
name: "Dragon Boats",
lft: 9,
rgt: 10
},
{
id: 7,
name: "Kayaks",
lft: 11,
rgt: 12
},
{
id: 8,
name: "Speedboats",
lft: 13,
rgt: 14
},
{
id: 9,
name: "Other Products",
lft: 16,
rgt: 21
},
{
id: 10,
name: "Slides",
lft: 17,
rgt: 18
},
{
id: 11,
name: "Buoys",
lft: 19,
rgt: 20
}
]
I want it to have an output something like this (or an array if there are multiple root nodes):
{
id: 1,
name: "Products",
subcategories: [
{
id: 2,
name: "Boats",
subcategories: [
{
id: 3,
name: "Rescue Boats"
},
{
id: 4,
name: "Dive Boats"
},
{
id: 5,
name: "Tamarans"
},
{
id: 6,
name: "Dragon Boats"
},
{
id: 7,
name: "Kayaks"
},
{
id: 8,
name: "Speedboats"
}
]
},
{
id: 9,
name: "Other Products",
subcategories: [
{
id: 10,
name: "Slides"
},
{
id: 11,
name: "Buoys",
}
]
}
]
}
I wrote this recursive solution. Hope it can help. If you have any doubt please ask me.
I just check if an element is inside right and left property of another one. In this case I add it into his fathers (and each anchestor). I repeat the same operation until there are no more "fathers".
const categories = [
{
id: 1,
name: "Products",
lft: 1,
rgt: 22
},
{
id: 2,
name: "Boats",
lft: 2,
rgt: 15
},
{
id: 3,
name: "Rescue Boats",
lft: 3,
rgt: 4
},
{
id: 4,
name: "Dive Boats",
lft: 5,
rgt: 6
},
{
id: 5,
name: "Tamarans",
lft: 7,
rgt: 8
},
{
id: 6,
name: "Dragon Boats",
lft: 9,
rgt: 10
},
{
id: 7,
name: "Kayaks",
lft: 11,
rgt: 12
},
{
id: 8,
name: "Speedboats",
lft: 13,
rgt: 14
},
{
id: 9,
name: "Other Products",
lft: 16,
rgt: 21
},
{
id: 10,
name: "Slides",
lft: 17,
rgt: 18
},
{
id: 11,
name: "Buoys",
lft: 19,
rgt: 20
}
]
function create_tree(array){
if(array.every(x => array.every(y => !(y.lft > x.lft && y.rgt < x.rgt))))
return array.map(x => {return{id:x.id, name:x.name, subcategories: x.subcategories}});
else
return create_tree(array.map(x => {return {
id: x.id,
name: x.name,
lft: x.lft,
rgt: x.rgt,
subcategories: array.filter(y => y.lft > x.lft && y.rgt < x.rgt).map(t => {return (t.subcategories) ? {id: t.id, name: t.name, subcategories: t.subcategories} : {id: t.id, name: t.name}})
}}).filter(t => t.subcategories && t.subcategories.length > 0));
}
console.log(create_tree(categories));

Create nested array data from an array of objects

I have an array of objects that has information of nested data, and I want to convert the data to actual nested array data.
How can I convert this:
const data = [
{id: 1, parent_id: null, name: 'test1'},
{id: 2, parent_id: null, name: 'test2'},
{id: 3, parent_id: 2, name: 'test3'},
{id: 4, parent_id: 2, name: 'test4'},
{id: 5, parent_id: 4, name: 'test5'},
{id: 6, parent_id: 4, name: 'test5'},
{id: 7, parent_id: 2, name: 'test5'},
{id: 8, parent_id: 2, name: 'test5'},
{id: 9, parent_id: null, name: 'test5'},
{id: 10, parent_id: null, name: 'test5'},
]
to this:
const data = [
{id: 1, parent_id: null, name: 'test1'},
{
id: 2,
parent_id: null,
name: 'test2',
children: [
{id: 3, parent_id: 2, name: 'test3'},
{
id: 4,
parent_id: 2,
name: 'test4',
children: [
{id: 5, parent_id: 4, name: 'test5'},
{id: 6, parent_id: 4, name: 'test5'}
]
},
{id: 7, parent_id: 2, name: 'test5'},
{id: 8, parent_id: 2, name: 'test5'},
]
},
{id: 9, parent_id: null, name: 'test5'},
{id: 10, parent_id: null, name: 'test5'},
]
What is the best way to do this?
You could create recursive function with reduce method for this.
const data = [{id: 1, parent_id: null, name: 'test1'},{id: 2, parent_id: null, name: 'test2'},{id: 3, parent_id: 2, name: 'test3'},{id: 4, parent_id: 2, name: 'test4'},{id: 5, parent_id: 4, name: 'test5'},{id: 6, parent_id: 4, name: 'test5'},{id: 7, parent_id: 2, name: 'test5'},{id: 8, parent_id: 2, name: 'test5'},{id: 9, parent_id: null, name: 'test5'},{id: 10, parent_id: null, name: 'test5'},]
function nest(data, parentId = null) {
return data.reduce((r, e) => {
let obj = Object.assign({}, e)
if (parentId == e.parent_id) {
let children = nest(data, e.id)
if (children.length) obj.children = children
r.push(obj)
}
return r;
}, [])
}
console.log(nest(data))
You could take a single loop approach by using an object and the id and parent_id as key and collect the items/children to it.
The order is only important for the order in the children array.
const
data = [{ id: 1, parent_id: null, name: 'test1' }, { id: 2, parent_id: null, name: 'test2' }, { id: 3, parent_id: 2, name: 'test3' }, { id: 4, parent_id: 2, name: 'test4' }, { id: 5, parent_id: 4, name: 'test5' }, { id: 6, parent_id: 4, name: 'test5' }, { id: 7, parent_id: 2, name: 'test5' }, { id: 8, parent_id: 2, name: 'test5' }, { id: 9, parent_id: null, name: 'test5' }, { id: 10, parent_id: null, name: 'test5' }],
tree = function (data, root) {
var t = {};
data.forEach(o => {
Object.assign(t[o.id] = t[o.id] || {}, o);
t[o.parent_id] = t[o.parent_id] || {};
t[o.parent_id].children = t[o.parent_id].children || [];
t[o.parent_id].children.push(t[o.id]);
});
return t[root].children;
}(data, null);
console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }
This is an interesting problem. One option if you want to keep linear time at the expense of some space it to make a lookup object based on id. Then you can loop through those values and push into either a parent object or the array:
const data = [{id: 1, parent_id: null, name: 'test1'},{id: 2, parent_id: null, name: 'test2'},{id: 3, parent_id: 2, name: 'test3'},{id: 4, parent_id: 2, name: 'test4'},{id: 5, parent_id: 4, name: 'test5'},{id: 6, parent_id: 4, name: 'test5'},{id: 7, parent_id: 2, name: 'test5'},{id: 8, parent_id: 2, name: 'test5'},{id: 9, parent_id: null, name: 'test5'},{id: 10, parent_id: null, name: 'test5'},]
let lookup = data.reduce((obj, item) => {
obj[item.id] = item
return obj
}, {})
let arr = Object.values(lookup).reduce((arr, val) =>{
if (val.parent_id == null) arr.push(val)
else (lookup[val.parent_id].children || ( lookup[val.parent_id].children = [])).push(val)
return arr
}, [])
console.log(JSON.stringify(arr, null, 2))
you could try this recursive approach
const data = [{id: 1, parent_id: null, name: 'test1'}, {id: 2, parent_id: null, name: 'test2'}, {id: 3, parent_id: 2, name: 'test3'}, {id: 4, parent_id: 2, name: 'test4'}, {id: 5, parent_id: 4, name: 'test5'}, {id: 6, parent_id: 4, name: 'test5'}, {id: 7, parent_id: 2, name: 'test5'}, {id: 8, parent_id: 2, name: 'test5'}, {id: 9, parent_id: null, name: 'test5'}, {id: 10, parent_id: null, name: 'test5'}];
const transform = arr => {
return arr.reduce((acc, elem) => {
const children = data.filter(el => el.parent_id === elem.id),
isPresent = findDeep(acc, elem);
if(!isPresent && children.length)
acc.push({...elem, children: transform(children)});
else if(!isPresent)
acc.push(elem);
return acc;
}, []);
}
const findDeep =(arr = [], elem) => (
arr.some(el => (el.id === elem.id) || findDeep(el.children, elem))
);
console.log(transform(data));
const data = [
{id: 1, parent_id: null, name: 'test1'},
{id: 2, parent_id: null, name: 'test2'},
{id: 3, parent_id: 2, name: 'test3'},
{id: 4, parent_id: 2, name: 'test4'},
{id: 5, parent_id: 4, name: 'test5'},
{id: 6, parent_id: 4, name: 'test5'},
{id: 7, parent_id: 2, name: 'test5'},
{id: 8, parent_id: 2, name: 'test5'},
{id: 9, parent_id: null, name: 'test5'},
{id: 10, parent_id: null, name: 'test5'},
]
const output = data.filter(
item => !item.parent_id
).map(
rootItem => ({
...rootItem,
children: data.filter(item => item.parent_id === rootItem.id),
})
)
console.log(output)

How to generate Child keys by Parents keys in Array

JavaScript ninjas! Now i have this collection:
var cats = [
{ id: 1, parent_id: 0, title: 'Movies' },
{ id: 2, parent_id: 0, title: 'Music' },
{ id: 3, parent_id: 1, title: 'Russian movies' },
{ id: 4, parent_id: 2, title: 'Russian music' },
{ id: 5, parent_id: 3, title: 'New' },
{ id: 6, parent_id: 3, title: 'Top10' },
{ id: 7, parent_id: 4, title: 'New' },
{ id: 8, parent_id: 4, title: 'Top10' },
{ id: 9, parent_id: 0, title: 'Soft' }
];
And i need this result:
var catsExtended = [
{ id: 1, parent_id: 0, childs: [ 3, 5, 6 ], title: 'Movies' },
{ id: 2, parent_id: 0, childs: [ 4, 7, 8 ], title: 'Music' },
{ id: 3, parent_id: 1, childs: [ 5, 6 ], title: 'Russian movies' },
{ id: 4, parent_id: 2, childs: [ 7, 8 ], title: 'Russian music' },
{ id: 5, parent_id: 3, childs: [], title: 'New' },
{ id: 6, parent_id: 3, childs: [], title: 'Top10' },
{ id: 7, parent_id: 4, childs: [], title: 'New' },
{ id: 8, parent_id: 4, childs: [], title: 'Top10' },
{ id: 9, parent_id: 0, childs: [], title: 'Soft' }
];
Help me pleace to collect all IDs
You could use a hash table for the reference to the already returned objects. And for the parents just iterate until parent_id becomes zero.
var cats = [{ id: 1, parent_id: 0, title: 'Movies' }, { id: 2, parent_id: 0, title: 'Music' }, { id: 3, parent_id: 1, title: 'Russian movies' }, { id: 4, parent_id: 2, title: 'Russian music' }, { id: 5, parent_id: 3, title: 'New' }, { id: 6, parent_id: 3, title: 'Top10' }, { id: 7, parent_id: 4, title: 'New' }, { id: 8, parent_id: 4, title: 'Top10' }, { id: 9, parent_id: 0, title: 'Soft' }],
catsExtended = cats.map(function (a) {
var id = a.parent_id;
this[a.id] = { id: a.id, parent_id: a.parent_id, children: [], title: a.title };
while (id) {
this[id].children.push(a.id);
id = this[id].parent_id;
}
return this[a.id];
}, Object.create(null));
console.log(catsExtended);
Combine map() and filter():
var catsExtended = cats.map(function(cat) {
return {
id: cat.id,
parent_id: cat.parent_id,
title: cat.title,
childs: cats.filter(function(c) {
return c.parent_id == cat.id;
}).map(function(c) {
return c.id
})
};
});
I think a simple Array.prototype.forEach can do a lot.
var cats = [{ id: 1, parent_id: 0, title: 'Movies' }, { id: 2, parent_id: 0, title: 'Music' }, { id: 3, parent_id: 1, title: 'Russian movies' }, { id: 4, parent_id: 2, title: 'Russian music' }, { id: 5, parent_id: 3, title: 'New' }, { id: 6, parent_id: 3, title: 'Top10' }, { id: 7, parent_id: 4, title: 'New' }, { id: 8, parent_id: 4, title: 'Top10' }, { id: 9, parent_id: 0, title: 'Soft' }];
cats.forEach(function(c) {
var pid = c.parent_id;
c.children = (this[c.id] = this[c.id] || []);
(this[pid] = (this[pid] || [])).push(c.id)
}, Object.create(null));
console.clear();
console.log(cats);

Categories