javascriptarraysreactjsd3.jsnivo-react

Converting array of object to hierarchical data structure


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.


Solution

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