4

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.

3
  • @a.mola: see answer of mplungjan for a non recursive solution. 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. Commented Mar 16, 2021 at 12:09
  • 1
    @trincot You're right. Changed the tag. Commented Mar 16, 2021 at 13:02

6 Answers 6

3

I 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);

Sign up to request clarification or add additional context in comments.

2 Comments

Nice and simple.
Thanks. I updated this solution a bit by deep cloning 'e' object and deleting children property from it. This is just in case I have a lot of properties, it'd be better to just remove the one children property. var i= JSON.parse(JSON.stringify(e)); delete i.subTrendingTopics; result.push(i);
3

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));

Comments

2

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));

2 Comments

This generates an order from child to parent. I needed parent to child. I think your previous solution gave the right output afaik. Anyway, thanks for the effort.
@PriyankerRao I’ve edited it now for Parent to Child
0

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))

1 Comment

Far too complex
0

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))

3 Comments

Updated the question to include an empty empty array for leaf nodes. This throws an error for the updated scenario. I could parse the object and delete an empty child array to make it work though.
@PriyankerRao The update handles empty children
I would never want to count on something like this, but I'm impressed that it works!
0

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)])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.