I have an array with nested objects having parent-child relationship like so:
[
{id: 1, title: 'hello', parent: 0, children: [
{id: 3, title: 'hello', parent: 1, children: [
{id: 4, title: 'hello', parent: 3, children: [
{id: 5, title: 'hello', parent: 4, children: []},
{id: 6, title: 'hello', parent: 4, children: []}
]},
{id: 7, title: 'hello', parent: 3, children: []}
]}
]},
{id: 2, title: 'hello', parent: 0, children: [
{id: 8, title: 'hello', parent: 2, children: []}
]}
]
I need to convert it into a plain array retaining the parent child relationship like so and in the order of parent and all its children returned first before proceeding on to the next parent.
[
{id: 1, title: 'hello', parent: 0},
{id: 3, title: 'hello', parent: 1},
{id: 4, title: 'hello', parent: 3},
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4},
{id: 7, title: 'hello', parent: 3},
{id: 2, title: 'hello', parent: 0},
{id: 8, title: 'hello', parent: 2}
]
I was able to convert the other way round with a recursive function.
But I need to do the opposite in an efficient way. There is multilevel nesting as shown in the sample nested array.
EDIT: Updated the nested array to have an empty children array for leaf nodes.
And also, an answer in ES5 would help.
I have an array with nested objects having parent-child relationship like so:
[
{id: 1, title: 'hello', parent: 0, children: [
{id: 3, title: 'hello', parent: 1, children: [
{id: 4, title: 'hello', parent: 3, children: [
{id: 5, title: 'hello', parent: 4, children: []},
{id: 6, title: 'hello', parent: 4, children: []}
]},
{id: 7, title: 'hello', parent: 3, children: []}
]}
]},
{id: 2, title: 'hello', parent: 0, children: [
{id: 8, title: 'hello', parent: 2, children: []}
]}
]
I need to convert it into a plain array retaining the parent child relationship like so and in the order of parent and all its children returned first before proceeding on to the next parent.
[
{id: 1, title: 'hello', parent: 0},
{id: 3, title: 'hello', parent: 1},
{id: 4, title: 'hello', parent: 3},
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4},
{id: 7, title: 'hello', parent: 3},
{id: 2, title: 'hello', parent: 0},
{id: 8, title: 'hello', parent: 2}
]
I was able to convert the other way round with a recursive function.
But I need to do the opposite in an efficient way. There is multilevel nesting as shown in the sample nested array.
EDIT: Updated the nested array to have an empty children array for leaf nodes.
And also, an answer in ES5 would help.
Share Improve this question edited Mar 16, 2021 at 13:23 Priyanker Rao asked Mar 16, 2021 at 8:26 Priyanker RaoPriyanker Rao 831 silver badge6 bronze badges 3- @a.mola: see answer of mplungjan for a non recursive solution. – MrSmith42 Commented Mar 16, 2021 at 9:16
-
1
Your question is tagged with
breadth-first-search
but your flat array is an output from a depth-first-search. – trincot Commented Mar 16, 2021 at 12:09 - 1 @trincot You're right. Changed the tag. – Priyanker Rao Commented Mar 16, 2021 at 13:02
6 Answers
Reset to default 3I just use a simple recursive function to make an array object into a plain array
var arr = [ {id: 1, title: 'hello', parent: 0, children: [ {id: 3, title: 'hello', parent: 1, children: [ {id: 4, title: 'hello', parent: 3, children: [ {id: 5, title: 'hello', parent: 4, children: []}, {id: 6, title: 'hello', parent: 4, children: []} ]}, {id: 7, title: 'hello', parent: 3, children: []} ]} ]}, {id: 2, title: 'hello', parent: 0, children: [ {id: 8, title: 'hello', parent: 2, children: []} ]} ];
var result = [];
var convertArrToObj = (arr) => {
arr.forEach(e => {
if (e.children) {
result.push({
id: e.id,
title: e.title,
parent: e.parent
});
convertArrToObj(e.children);
} else result.push(e);
});
};
convertArrToObj(arr);
console.log(result);
In ES5 you can also use some functional programming approach, and flatten an array with [].concat.apply
:
function flatten(arr) {
return [].concat.apply([], arr.map(function (obj) {
return [].concat.apply([
{ id: obj.id, title: obj.title, parent: obj.parent }
], flatten(obj.children));
}));
}
let arr = [{id: 1, title: 'hello', parent: 0, children: [{id: 3, title: 'hello', parent: 1, children: [{id: 4, title: 'hello', parent: 3, children: [{id: 5, title: 'hello', parent: 4, children: []},{id: 6, title: 'hello', parent: 4, children: []}]},{id: 7, title: 'hello', parent: 3, children: []}]}]},{id: 2, title: 'hello', parent: 0, children: [{id: 8, title: 'hello', parent: 2, children: []}]}];
console.log(flatten(arr));
In ES6 the same algorithm reduces to the following:
const flatten = arr => arr.flatMap(({children, ...o}) => [o, ...flatten(children)]);
let arr = [{id: 1, title: 'hello', parent: 0, children: [{id: 3, title: 'hello', parent: 1, children: [{id: 4, title: 'hello', parent: 3, children: [{id: 5, title: 'hello', parent: 4, children: []},{id: 6, title: 'hello', parent: 4, children: []}]},{id: 7, title: 'hello', parent: 3, children: []}]}]},{id: 2, title: 'hello', parent: 0, children: [{id: 8, title: 'hello', parent: 2, children: []}]}];
console.log(flatten(arr));
Using ES5 would require a lot more lines of code and like you said is not very efficient.
Here's my ES5 version, you should be able to notice the difference in performance
const data = [{id:1,title:'hello',parent:0,children:[{id:3,title:'hello',parent:1,children:[{id:4,title:'hello',parent:3,children:[{id:5,title:'hello',parent:4,children:[]},{id:6,title:'hello',parent:4,children:[]}]},{id:7,title:'hello',parent:3,children:[]}]}]},{id:2,title:'hello',parent:0,children:[{id:8,title:'hello',parent:2,children:[]}]}];
// Recursively
function reduceArrayDimension(array) {
var level = [];
array.forEach(function(item) {
level.push({
id: item.id,
title: item.title,
parent: item.parent
});
item.children.forEach(function(child) {
reduceArrayDimension([child]).forEach(function(childItem) {
level.push(childItem);
});
});
});
return level;
}
console.log(reduceArrayDimension(data));
And ES6
const data=[{id:1,title:'hello',parent:0,children:[{id:3,title:'hello',parent:1,children:[{id:4,title:'hello',parent:3,children:[{id:5,title:'hello',parent:4,children:[]},{id:6,title:'hello',parent:4,children:[]}]},{id:7,title:'hello',parent:3,children:[]}]}]},{id:2,title:'hello',parent:0,children:[{id:8,title:'hello',parent:2,children:[]}]}];
// Recursively
function reduceArrayDimension(array) {
const level = [];
array.forEach(item => {
level.push({id: item.id, title: item.title, parent: item.parent});
if (item.children) level.push(...reduceArrayDimension(item.children));
});
return level;
}
console.log(reduceArrayDimension(data));
if data is large can consider use tail optimization and async/await
const arr = [
{id: 1, title: 'hello', parent: 0, children: [
{id: 3, title: 'hello', parent: 1, children: [
{id: 4, title: 'hello', parent: 3, children: [
{id: 5, title: 'hello', parent: 4},
{id: 6, title: 'hello', parent: 4}
]},
{id: 7, title: 'hello', parent: 3}
]}
]},
{id: 2, title: 'hello', parent: 0, children: [
{id: 8, title: 'hello', parent: 2}
]}
];
const convertArr = (arr) => {
return arr.reduce((init, cur) => {
const plain = init.concat(cur);
const children = cur.children;
return plain.concat(children && children.length ? convertArr(children) : [])
}, [])
}
const generateArr = (arr) => {
return convertArr(arr).map(v => ({
id: v.id,
parent: v.parent,
title: v.title
}))
}
console.log('result:', generateArr(arr))
If the data is not very large, this could be a pragmatic method
const data = [{ id: 1, title: 'hello', parent: 0, children: [{ id: 3, title: 'hello', parent: 1, children: [{ id: 4, title: 'hello', parent: 3, children: [{ id: 5, title: 'hello', parent: 4 }, { id: 6, title: 'hello', parent: 4 , children:[]} ] }, { id: 7, title: 'hello', parent: 3 } ] }] }, { id: 2, title: 'hello', parent: 0, children: [{ id: 8, title: 'hello', parent: 2 }] } ];
const str = JSON.stringify(data)
.replaceAll('"children":[',"},")
.replaceAll("]}","")
.replaceAll(",,",",") // handle empty children
.replaceAll(",}","}");
console.log(JSON.parse(str).sort((a,b) => a.id-b.id))
You can use a generator function:
var arr = [ {id: 1, title: 'hello', parent: 0, children: [ {id: 3, title: 'hello', parent: 1, children: [ {id: 4, title: 'hello', parent: 3, children: [ {id: 5, title: 'hello', parent: 4, children: []}, {id: 6, title: 'hello', parent: 4, children: []} ]}, {id: 7, title: 'hello', parent: 3, children: []} ]} ]}, {id: 2, title: 'hello', parent: 0, children: [ {id: 8, title: 'hello', parent: 2, children: []} ]} ];
function* flatten(d){
for (var i of d){
yield {id:i.id, title:i.title, parent:i.parent}
yield* flatten(i.children)
}
}
console.log([...flatten(arr)])