1

I have the following data coming from the API:

[
  {
    "Code": "01002",
    "ParentAccountId": "01",
  },
  {
    "Code": "01001001003",
    "ParentAccountId": "01001001",
  },
  {
    "Code": "01001004",
    "ParentAccountId": "01001",
  },
  {
    "Code": "02",
    "ParentAccountId": null,
  },
  {
    "Code": "01002001",
    "ParentAccountId": "01002",
  },
  {
    "Code": "02002",
    "ParentAccountId": "02",
  },
  {
    "Code": "02001",
    "ParentAccountId": "02",
  },
  {
    "Code": "01001001001",
    "ParentAccountId": "01001001",
  },
  {
    "Code": "03",
    "ParentAccountId": null,
  },
  {
    "Code": "01002002",
    "ParentAccountId": "01002",
  },
  {
    "Code": "03001",
    "ParentAccountId": "03",
  },
  {
    "Code": "01",
    "ParentAccountId": null,
  },
  {
    "Code": "01001001002",
    "ParentAccountId": "01001001",
  },
  {
    "Code": "01001002",
    "ParentAccountId": "01001",
  },
  {
    "Code": "01001001",
    "ParentAccountId": "01001",
  },
  {
    "Code": "01001003",
    "ParentAccountId": "01001",
  },
  {
    "Code": "01001005",
    "ParentAccountId": "01001",
  },
  {
    "Code": "01001",
    "ParentAccountId": "01",
  }
]

Look at the ParentAccountId.

As I need to pass it to the treeview component so, I need to convert it to something like this:

    [
  {
    "Code": "01",
    "ParentAccountId": null,
    "children": [
        {
            "Code": "01001",
            "ParentAccountId": "01",
            "children": [
                  {
                    "Code": "01001001",
                    "ParentAccountId": "01001",
                    "children": [
                        {
                            "Code": "01001001001",
                            "ParentAccountId": "01001001",
                            "children": [],
                          },
                        {
                            "Code": "01001001002",
                            "ParentAccountId": "01001001",
                            "children": [],
                          },
                          {
                            "Code": "01001001003",
                            "ParentAccountId": "01001001",
                            "children": [],
                          },
                    ],
                  },
                {
                    "Code": "01001002",
                    "ParentAccountId": "01001",
                    "children": [],
                  },
                  {
                    "Code": "01001003",
                    "ParentAccountId": "01001",
                    "children": [],
                  },
                  {
                    "Code": "01001004",
                    "ParentAccountId": "01001",
                    "children": [],
                  },
                  {
                    "Code": "01001005",
                    "ParentAccountId": "01001",
                    "children": [],
                  }
            ],
          },
        {
            "Code": "01002",
            "ParentAccountId": "01",
            "children": [
                {
                    "Code": "01002001",
                    "ParentAccountId": "01002",
                    "children": [],
                  },
                {
                    "Code": "01002002",
                    "ParentAccountId": "01002",
                    "children": [],
                  },
            ],
          },
    ],
  },
  {
    "Code": "02",
    "ParentAccountId": null,
    "children": [
          {
            "Code": "02001",
            "ParentAccountId": "02",
            "children": [],
          },
        {
            "Code": "02002",
            "ParentAccountId": "02",
            "children": [],
          },
    ],
  },
  {
    "Code": "03",
    "ParentAccountId": null,
    "children": [
        {
            "Code": "03001",
            "ParentAccountId": "03",
            "children": [],
          },
    ],
  },
]

I want to make the the object as child of it's parent according the code. The scheme is if the ParentAccountId is null it's the top level parent, if the ParentAccountId is of length 2 then it's the 1st level child if the ParentAccountId is of length 5 then it's the 3rd level child then if ParentAccountId is of length 8 then it's 4th level child then ParentAccountId is of length 11 then it's 5th level child. As the 1st level child have 2 length of ParentAccountId then the subsequent children will have the ParentAccountId as Code of the parent plus. For better understading please see the second because my English is not that better.

I am confused about the logic. Any Suggestions?

U-Dev
  • 1,296
  • 5
  • 19

4 Answers4

2

You could create tree structure using reduce method to create recursive function where in each iteration you check if the parent id is equal to current element id.

const data = [{"Id":"1","Code":"01","Title":"Account 01","ParentAccountId":null},{"Id":"2","Code":"02","Title":"Account 02","ParentAccountId":null},{"Id":"3","Code":"01001","Title":"Account 01001","ParentAccountId":"01"},{"Id":"4","Code":"01002","Title":"Account 01002","ParentAccountId":"01"},{"Id":"5","Code":"01002001","Title":"Account 01002001","ParentAccountId":"01002"}]

function toTree(data, pid = null) {
  return data.reduce((r, e) => {
    if (e.ParentAccountId == pid) {
      const obj = { ...e };
      const children = toTree(data, e.Code);
      if (children.length) obj.children = children;
      r.push(obj);
    }
    return r;
  }, [])
}

const result = toTree(data)
console.log(result)
Nenad Vracar
  • 118,580
  • 15
  • 151
  • 176
  • Hey, @nenad your function it better and works for my multilevel data, can you explain about the performance that is it less performant than using a loop? If there is no performance issue than I will choose this solution otherwise I will go for a loop solution as I have much data. – U-Dev Oct 22 '19 at 09:46
1

The logic involved is to first try and find the children of every object (Accomplished using filter to find all objects that have a ParentAccountId equal to each objects Code) and then filter the data to return only the root parents (objects with ParentAccountId equal to null).

Try the code below.

var data = [{
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  },
  {
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  },
  {
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  },
  {
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  },
  {
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  }
]

rearrangeData = () => {

  var newData = []

  data.forEach((x) => {
    x['children'] = data.filter((y) => {
      return y.ParentAccountId === x.Code
    })
    var parent = data.find((y) => {
      return y.Code === x.ParentAccountId
    })
    if (parent && parent.children) {
      parent.children.push(x)
    } else if (parent && !parent.children) {
      parent['children'] = [x];
    } else {
      return x
    }
    newData.push(parent)
  })

  var parents = newData.filter((x) => {
    return x.ParentAccountId === null
  })

  console.log(parents);
}

rearrangeData()
Matt Croak
  • 2,788
  • 2
  • 17
  • 35
  • Bro, It only works for the first two children, check I have updated the data in my code, your code doesn't work for that data. – U-Dev Oct 22 '19 at 09:28
  • I edited the code snippet. It works now. You can also access the codepen [here](https://codepen.io/macro6461/pen/dyyNrpM) as the snippet cuts off some of the logged data. – Matt Croak Oct 22 '19 at 14:02
0

I'm waiting on a task at work so I figured I'd throw together an implementation of this for you. Not saying it's any better or worse than the solutions in the threads linked - just another implementation:

const data = [{
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  },
  {
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  },
  {
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  },
  {
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  },
  {
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  }
]

function buildTree(obj) {
  // get all top level parents
  let parents = obj.filter((o) => !o.ParentAccountId);
  // loop over the parents and recursively call addChild to populate the tree
  parents.forEach((p) => {
    p.children = addChildren(p, obj);
  });

  return parents;
}

function addChildren(parent, obj) {
  // find all children for this parent
  let children = obj.filter((o) => o.ParentAccountId === parent.Code)
  if (children.length) {
    // loop over any children recursively calling this function to add nested children
    children.forEach((c) => {
      c.children = addChildren(c, obj);
    });
    return children;
  } else {
    return [];
  }
}

console.log(buildTree(data));
Adam H
  • 1,750
  • 1
  • 9
  • 24
0

You can iterate over every node and build a map of child-id to child.

Then, loop over the nodes again, but this time looking at the parent-ids and pushing the node to the parent if it is a child, or adding it to the root list. This response is adapted from here with the addition of a custom key configuration.

I also added a method to remove the children field on any object that does not contain children i.e. leaf nodes.

console.log(convertListToTree(getDataList(), {
  idKey : 'Code',
  parentIdKey : 'ParentAccountId',
  pruneEmptyChildren : true
}));

function convertListToTree(list, options) {
  options = Object.assign({
    idKey : 'id',
    parentIdKey : 'parentId',
    childrenKey : 'children',
    pruneEmptyChildren : false
  }, options || {});
  let map = {}, node, roots = [], i;
  for (i = 0; i < list.length; i++) {
    map[list[i][options.idKey]] = i;
    list[i][options.childrenKey] = []; // Attach a "child" reference holder
  }
  for (i = 0; i < list.length; i++) {
    node = list[i];
    if (node[options.parentIdKey] != null) {
      list[map[node[options.parentIdKey]]][options.childrenKey].push(node);
    } else {
      roots.push(node);
    }
  }
  if (options.pruneEmptyChildren) {
    pruneEmptyKeys(roots, options.childrenKey); // Remove empty
  }
  return roots;
}

function pruneEmptyKeys(tree, childKey) {
  let items = tree[childKey] || tree;
  items.forEach(item => {
    if (item[childKey].length > 0) {
      pruneEmptyKeys(item[childKey], childKey);
    } else {
      delete item[childKey]; // Remove empty child list
    }
  });
}

function getDataList() {
  return [{
    "Id": "1",
    "Code": "01",
    "Title": "Account 01",
    "ParentAccountId": null
  }, {
    "Id": "2",
    "Code": "02",
    "Title": "Account 02",
    "ParentAccountId": null
  }, {
    "Id": "3",
    "Code": "01001",
    "Title": "Account 01001",
    "ParentAccountId": "01"
  }, {
    "Id": "4",
    "Code": "01002",
    "Title": "Account 01002",
    "ParentAccountId": "01"
  }, {
    "Id": "5",
    "Code": "01002001",
    "Title": "Account 01002001",
    "ParentAccountId": "01002"
  }];
}
.as-console-wrapper {
  top: 0;
  max-height: 100% !important;
}
<!-- Adapted from: https://stackoverflow.com/a/18018037/1762224 -->
Mr. Polywhirl
  • 42,981
  • 12
  • 84
  • 132