regenerate source array with objects to result recursively (JS)

Viewed 48

Hi there, I need your help with recursion. I have a source array with objects and I need to regenerate it to result array view. Need to change some data structure.

I've tried to do that on my own but no results for now;(

Each element may have a parrent_id field, nesting can be infinite. How can I do that with recursion?

const source = [
  {
    id: 1,
    title: 'title1',
    alias: 'alias1',
    parent_id: null
  },
  {
    id: 2,
    title: 'title2',
    alias: 'alias2',
    parent_id: null
  },
  {
    id: 3,
    title: 'title3',
    alias: 'alias3',
    parent_id: 2
  },
  {
    id: 4,
    title: 'title4',
    alias: 'alias4',
    parent_id: 2
  },
  {
    id: 5,
    title: 'title5',
    alias: 'alias5',
    parent_id: 4
  },
  {
    id: 6,
    title: 'title6',
    alias: 'alias6',
    parent_id: 4
  }
];

const result = [
  {
    id: 1,
    alias: 'alias',
    title: 'root cat',
    link: '/',
    Icon: 'icon'
  },
  {
    id: 2,
    alias: 'alias',
    title: 'Nested Pages',
    Icon: 'icon',
    items: [
      {
        id: 3,
        alias: 'alias',
        title: 'sub cat for Nested Pages'
      },
      {
        id: 4,
        alias: 'alias',
        title: 'sub cat for Nested Pages',
        items: [
          {
            id: 5,
            alias: 'alias',
            title: 'sub sub cat '
          },
          {
            id: 6,
            alias: 'alias',
            title: 'Level 3'
          }
        ]
      }
    ]
  }
];

Thnx in advance

P.S. Here is my code, it works just for one level nesting. How to solve it to use recursively?

let finArray = [];
const regenArray = (categories, parrent_id = null) => {
  categories.map((el, index, array) => {
    if (el.parent_id === parrent_id) {
      finArray.push(el);
      finArray[finArray.length - 1]['items'] = [];
      categories.map(
        (child_el, child_index, child_array) => {
          if (el.id === child_el.parent_id) {
            finArray[finArray.length - 1]['items'].push(
              child_el
            );
          }
          return false;
        }
      );
    }
  });
};
regenArray(categories);

console.log('finArray', finArray);
3 Answers

You do not need a recursion, just an object for keeping the reference to node and parent node.

This approach works for unsorted data as well, because it uses both relation from item to parent and from parent to item.

const
    source = [{ id: 1, title: 'title1', alias: 'alias1', parent_id: null }, { id: 2, title: 'title2', alias: 'alias2', parent_id: null }, { id: 3, title: 'title3', alias: 'alias3', parent_id: 2 }, { id: 4, title: 'title4', alias: 'alias4', parent_id: 2 }, { id: 5, title: 'title5', alias: 'alias5', parent_id: 4 }, { id: 6, title: 'title6', alias: 'alias6', parent_id: 4 }],
    tree = function (data, root) {
        var t = {};
        data.forEach(({ parent_id, ...o }) => {
            Object.assign(t[o.id] = t[o.id] || {}, o);
            t[parent_id] = t[parent_id] || {};
            t[parent_id].items = t[parent_id].items || [];
            t[parent_id].items.push(t[o.id]);
        });
        return t[root].items;
    }(source, null);

console.log(tree);
.as-console-wrapper { max-height: 100% !important; top: 0; }

Iterate through each object of resultant array.if there is no match in objects id to current object's parent id ,do a recursive call to check its items.

 function findParentAndPushChild(object,resArray){
    resArray.forEach( (child)=> {
    if(child.id == object.parent_id)child.items.push(object);
    else if(child.items.length!=0)findParentAndPushChild(object,child.items);
    });
 }

Check the following code snippet.

const source = [{  id: 1,title:'title1',alias:'alias1',parent_id: null},{id: 2,title:'title2',alias: 'alias2',parent_id: null},{id: 3,title:'title3',alias:'alias3',parent_id: 2},{ id: 4,title:'title4',alias:'alias4',parent_id: 2},{id: 5,title:'title5',alias:'alias5',parent_id: 4},{id: 6,title:'title6',alias:'alias6',parent_id: 4}
  ];
let resultArray=[];
source.map( obj =>{obj.items=[];return obj}).forEach(obj=>{
     if(obj.parent_id==null)resultArray.push(obj);
     else{
        let index=resultArray.findIndex( (resObj)=>resObj.id==obj.parent_id);
        if( index == -1)findParentAndPushChild(obj,resultArray) 
        else resultArray[index].items.push(obj);
        }
})
 function findParentAndPushChild(object,resArray){
    resArray.forEach( (child)=> {if(child.id == object.parent_id)child.items.push(object);
    else if(child.items.length!=0)findParentAndPushChild(object,child.items);
    });
 } 
 console.log(resultArray);
.as-console-wrapper { max-height: 100% !important; top: 0; }

First I was trying a tree, but after seen @nina-scholz answer. I decided to write the same code in ES6 manner.

const source = [
  { id: 1, title: "title1", alias: "alias1", parent_id: null },
  { id: 2, title: "title2", alias: "alias2", parent_id: null },
  { id: 3, title: "title3", alias: "alias3", parent_id: 2 },
  { id: 4, title: "title4", alias: "alias4", parent_id: 2 },
  { id: 5, title: "title5", alias: "alias5", parent_id: 4 },
  { id: 6, title: "title6", alias: "alias6", parent_id: 4 }
];
const [result] = source.reduce(
  ([r, map], item) => {
    const d = { ...item, children: [] };
    const loc = map[item.parent_id];
    if (loc) {
      loc.children.push(d);
    } else {
      r.push(d);
    }
    map[item.id] = d;
    return [r, map];
  },
  [[], {}]
);
console.log(JSON.stringify(result, null, 4));
.as-console-row {color: blue!important}

Related