最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to convert a nested array with parent child relationship to a plain array? - Stack Overflow

programmeradmin1浏览0评论

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
Add a ment  | 

6 Answers 6

Reset to default 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);

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

发布评论

评论列表(0)

  1. 暂无评论