0

I have a flatter array like this

const arr = 
  [ { org_level_code: 'BBI',   org_level_name: 'Bostwick',     org_level_parent_code: 'toplevel',      children: null } 
  , { org_level_code: 'CDI',   org_level_name: 'BBi US',       org_level_parent_code: 'BBI',           children: null } 
  , { org_level_code: '60',    org_level_name: '60 - Sidney1', org_level_parent_code: 'BBI/CDI',       children: null } 
  , { org_level_code: 'BBC',   org_level_name: 'BBi Canada',   org_level_parent_code: 'BBI/CDI',       children: null } 
  , { org_level_code: 'BBI-2', org_level_name: 'BBi U.S. ',    org_level_parent_code: 'BBI/CDI',       children: null } 
  , { org_level_code: 'DEPT',  org_level_name: 'Dept',         org_level_parent_code: 'BBI/CDI/BBI-2', children: null } 
  ]

From this array I need the array like this

const data = 
  [ { org_level_code: 'BBI',              org_level_name: 'Bostwick',   org_level_parent_code: 'toplevel',     children: 
      [ { org_level_code: 'CDI',          org_level_name: 'BBi US',     org_level_parent_code: 'BBI',          children: 
          [ { org_level_code: 'BBC',      org_level_name: 'BBi Canada', org_level_parent_code: 'BBI/CDI',       children: null } 
          , { org_level_code: '60',    org_level_name: '60 - Sidney1',  org_level_parent_code: 'BBI/CDI',       children: null } 
          , { org_level_code: 'BBI-2',    org_level_name: 'BBi U.S. ',  org_level_parent_code: 'BBI/CDI',      children: 
              [ { org_level_code: 'DEPT', org_level_name: 'Dept',       org_level_parent_code: 'BBI/CDI/BBI-2', children: null }
  ] } ] } ] } ] 

As you can see in the example "org_level_parent_code": "toplevel" is the parent node and "org_level_parent_code": "BBI" is the second node of parent because first node has the relation with child one with "org_level_code": "BBI" and "org_level_parent_code": "BBI/CDI" indicates the 3rd level. These slash separated string has the relation with their respective parent nodes.

The last string e.g. BBI-2 of the slash separated string has the relation with above node.

Note- Top level node can be multiple like "org_level_parent_code": "toplevel".

I am really struggling hard to achieve this. Please help.

Nectar Mind
  • 145
  • 2
  • 17
  • Does this answer your question? [Build tree array from flat array in javascript](https://stackoverflow.com/questions/18017869/build-tree-array-from-flat-array-in-javascript) – James Jun 03 '21 at 17:26
  • @James no I have tried this but that one is quite different. I don't have the direct relation between with nodes like parentId and all. – Nectar Mind Jun 03 '21 at 17:30
  • You could look up the appropriate parent from the current node’s `org_level_parent_code` using a technique like [this](https://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-and-arrays-by-string-path) – James Jun 03 '21 at 17:36

3 Answers3

1

You can use a Map, to store node location, if next node found a parent node, push to that parent node.

const arr = [{ "org_level_code": "BBI", "org_level_name": "Bostwick", "org_level_parent_code": "toplevel", "children": null }, { "org_level_code": "BBI", "org_level_name": "BBi US", "org_level_parent_code": "BBI", "children": null }, { "org_level_code": "60", "org_level_name": "60 - Sidney1", "org_level_parent_code": "BBI/BBI", "children": null }, { "org_level_code": "BBC", "org_level_name": "BBi Canada", "org_level_parent_code": "BBI/BBI", "children": null }, { "org_level_code": "BBI-2", "org_level_name": "BBi U.S. ", "org_level_parent_code": "BBI/BBI", "children": null }, { "org_level_code": "DEPT", "org_level_name": "Dept", "org_level_parent_code": "BBI/BBI/BBI-2", "children": null }];

let map = new Map;
let result = arr.shift();
for (const item of arr) {
  item.children ??= []
  let parentId = item.org_level_parent_code;
  map.set(parentId + "/" + item.org_level_code, item);
  map.get(parentId)?.children.push(item);
}
result.children = [map.get("BBI/BBI")];

console.log(result);
Nur
  • 2,361
  • 2
  • 16
  • 34
  • Hi Nur thanks for your help. But "org_level_parent_code" could be anything like YANKS/CINC or YANKS/CINC/YUI. So we will identify or map while iteration. – Nectar Mind Jun 03 '21 at 19:25
  • This is sensitive to order, if an item is encountered before its parent it will not be included in the result. – pilchard Jun 04 '21 at 11:14
-1

We can add the nodes based on levels. Here is the solutions:-

const orgGreenTree = (array) => {
let levels = [{}];
array.forEach(function(a) {
  let level;
  if (a.org_level_parent_code === 'toplevel') {
    level = 0;
  }
  else {
    level = (a.org_level_parent_code.match(/\//g) || []).length + 1;
  }
  levels.length = level + 1;
  // console.log(level)
  levels[level].children = levels[level].children || [];
  levels[level].children.push(a);
  levels[level + 1] = a;
});
 return levels[0].children;
};
console.log(orgGreenTree(array));
Nectar Mind
  • 145
  • 2
  • 17
-1

Here is concise, more generalized solution using Array#reduce().

It accumulates into an Object whose properties are the paths to each item's children array. Top level items are accumulated into the toplevel child array which is the final result.

const
  arr = [{ org_level_code: 'BBI', org_level_name: 'Bostwick', org_level_parent_code: 'toplevel', children: null }, { org_level_code: 'CDI', org_level_name: 'BBi US', org_level_parent_code: 'BBI', children: null }, { org_level_code: '60', org_level_name: '60 - Sidney1', org_level_parent_code: 'BBI/CDI', children: null }, { org_level_code: 'BBC', org_level_name: 'BBi Canada', org_level_parent_code: 'BBI/CDI', children: null }, { org_level_code: 'BBI-2', org_level_name: 'BBi U.S. ', org_level_parent_code: 'BBI/CDI', children: null }, { org_level_code: 'DEPT', org_level_name: 'Dept', org_level_parent_code: 'BBI/CDI/BBI-2', children: null }],

  result = arr
    .reduce((acc, item) => {

      const parentCode = item.org_level_parent_code;
      const childrenPath = `${parentCode === 'toplevel' ? '' : parentCode + '/'}${item.org_level_code}`;

      acc[childrenPath] ??= [];
      (acc[parentCode] ??= []).push({ ...item, children: acc[childrenPath] })

      return acc;
    }, {})
    .toplevel

console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }

This solution is not dependent on the order of the input array, and will correctly accumulate children even if the parent item hasn't been encountered yet.

const
  arr = [
    { org_level_code: 'DEPT', org_level_name: 'Dept', org_level_parent_code: 'BBI/CDI/BBI-2', children: null }
    , { org_level_code: 'CDI', org_level_name: 'BBi US', org_level_parent_code: 'BBI', children: null }
    , { org_level_code: '60', org_level_name: '60 - Sidney1', org_level_parent_code: 'BBI/CDI', children: null }
    , { org_level_code: 'BBI', org_level_name: 'Bostwick', org_level_parent_code: 'toplevel', children: null }
    , { org_level_code: 'BBC', org_level_name: 'BBi Canada', org_level_parent_code: 'BBI/CDI', children: null }
    , { org_level_code: 'BBI-2', org_level_name: 'BBi U.S. ', org_level_parent_code: 'BBI/CDI', children: null }
  ],

  result = arr
    .reduce((acc, item) => {

      const parentCode = item.org_level_parent_code;
      const childrenPath = `${parentCode === 'toplevel' ? '' : parentCode + '/'}${item.org_level_code}`;

      acc[childrenPath] ??= [];
      (acc[parentCode] ??= []).push({ ...item, children: acc[childrenPath] })

      return acc;
    }, {})
    .toplevel

console.log(JSON.stringify(result, null, 2));
.as-console-wrapper { max-height: 100% !important; top: 0; }
pilchard
  • 12,414
  • 5
  • 11
  • 23