Converting array of object to hierarchical data structure

928 Views Asked by At

I have an original array and I want to plot it in Sunburst map which needs a hierarchical data structure.

[
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2},
]

Desired Result

[
  {
   id: Asia,
   children:[{
     id: India,
     children:[{
       id: Delhi,
       value: 41,
        }]
     },
     {
      id:China,
      children:[{
        id: Beijing
        value: 2,
        }]
     }]
  },
  {
    id: Europe,
    children: [{
       id: Germany,
       children: [{
          id: Berlin,
          value: 24,
       }]
    },
    {
       id: England,
       children: [{
         id: London,
         value: 3,
       }]
    }]
  },
  {
   id: NorthAmerica,
   children:[{
      id: USA,
      children:[{
        id: NewYork,
        value: 4, 
      },
      {
        id: Boston,
        value: 3,
      },
      {
        id: Chicago,
        value: 3,
      }]
   }]
  },
  {
   id: Austrailia
   children: [{
     id:Sydney,
     value: 4,
     }]
  },
]

can anyone help me with this, I tried using reduce method but I am not able to get the desired result.

PS : It would be super useful if anyone could suggest an answer that would deal with n number of ids separated by commas. For ex: here we have 3 id hierarchy separated by commas, what would happen if there were 4 or 5 depth data.

2

There are 2 best solutions below

0
On BEST ANSWER

To build a hierarchy of objects from your input is fairly straightforward, you dont even need to do anything recursive a loop + reduce will do it. This will work with any number of levels in your comma separated list.

const input = [
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2}
]
const result = input.map(o => ({ids:o.id.split(","), value:o.value})).reduce( (acc,obj) => {
   let curr = acc;
   let id;
   while( (id = obj.ids.shift()) != null ){
     if(!curr[id])
        curr[id] = {};
        
     curr = curr[id];
   }
   curr.value = obj.value
   return acc;
},{});
console.log(result);
.as-console-wrapper { max-height: 100% !important; top: 0; }

To then turn this into the format you wanted does take a bit of recursion:

const input = [
{id: "Asia,India,NewDelhi", value: 41},
{id: "Europe,Germany,Berlin", value: 24},
{id: "Europe,England,London", value: 3},
{id: "NorthAmerica,USA,NewYork", value: 4},
{id: "NorthAmerica,USA,Boston", value: 3},
{id: "NorthAmerica,USA,chicago", value: 3},
{id: "Austrailia,Sydney", value: 4},
{id: "Asia,China,Beijing", value: 2}
]
const result = input.map(o => ({ids:o.id.split(","), value:o.value})).reduce( (acc,obj) => {
   let curr = acc;
   let id;
   while( (id = obj.ids.shift()) != null ){
     if(!curr[id])
        curr[id] = {};
        
     curr = curr[id];
   }
   curr.value = obj.value
   return acc;
},{});

function buildHierarchy(input){
  return Object.entries(input).map( ([id,children]) => {
    if(children.value){
      return {id,value:children.value}
    }
    return {id, children: buildHierarchy(children)}
  })
}

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

0
On

A simple solution with recursion:

const data = [
  {id: "Asia,India,NewDelhi", value: 41},
  {id: "Europe,Germany,Berlin", value: 24},
  {id: "Europe,England,London", value: 3},
  {id: "NorthAmerica,USA,NewYork", value: 4},
  {id: "NorthAmerica,USA,Boston", value: 3},
  {id: "NorthAmerica,USA,Chicago", value: 3},
  {id: "Austrailia,Sydney", value: 4},
  {id: "Asia,China,Beijing", value: 2},
];

const addChild = (ids, value, arr) => {
  const id = ids.shift();
  let index = arr.findIndex(item => item.id === id);
  if (index < 0) {
    arr.push({id, children: []});
    index = arr.length - 1;
  }
  if (ids.length > 0) {
    const children = arr[index].children;
    addChild(ids, value, children);
  }
  else
    arr[index].value = value;
}

const treeData = data.reduce((tree, item) => {
  const ids = item.id.split(',');
  addChild(ids, item.value, tree);
  return tree;
}, []);

console.log(treeData);