7

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?

5 Answers 5

6

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

3

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

5
  • Does this depend on the order? For example if id:1 has a parent of 4, it seems like it ends up in the wrong place.
    – Mark
    Nov 20, 2018 at 15:48
  • @MarkMeyer, the order does not matter for parent children relations. Nov 20, 2018 at 15:52
  • Hmm, not sure what it is. If I change the parent of item id:1 from null to 4 it ends up in the wrong place.
    – Mark
    Nov 20, 2018 at 16:01
  • please look below, children array comes first, but the item is in the right place. Nov 20, 2018 at 16:04
  • I apologize — you are right! Just couldn't read my own output :(.
    – Mark
    Nov 20, 2018 at 16:06
1

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

0

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

-1

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)

2
  • 1
    What happened it id: 5?
    – Mark
    Nov 20, 2018 at 15:37
  • Ah you're right there is more than one level of nesting, the best answer is probably the one using reduce. Nov 20, 2018 at 15:39

Your Answer

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

Not the answer you're looking for? Browse other questions tagged or ask your own question.