Skip to content

How to create hierarchy data in JavaScript by providing dynamic groups and summing the value?

I want to create a dynamic hierarchy based on my groups and rollup sum to top level, also if I am providing a large amount of data browser is getting hang.

I have the below data:

 var data = [
   {
      "country":"Spain",
      "orderNumber":10394,
      "year":2018,
      "countrycode":"es",
      "quantityOrdered":30,
      "priceEach":60.28,
      "productName":"1950's Chicago Surface Lines Streetcar",
      "productLine":"Trains",
      "sortkey":3,
      "productCode":"S32_3207",
      "month":"March",
      "extendedPrice":1808.4,
      "orderDate":"2018-03-15 00:00:00"
   },
   {
      "country":"France",
      "orderNumber":10395,
      "year":2018,
      "countrycode":"fr",
      "quantityOrdered":32,
      "priceEach":105.33,
      "productName":"1972 Alfa Romeo GTA",
      "productLine":"Classic Cars",
      "sortkey":3,
      "productCode":"S10_4757",
      "month":"March",
      "extendedPrice":3370.56,
      "orderDate":"2018-03-17 00:00:00"
   },
   {
      "country":"France",
      "orderNumber":10395,
      "year":2018,
      "countrycode":"fr",
      "quantityOrdered":33,
      "priceEach":69.12,
      "productName":"2001 Ferrari Enzo",
      "productLine":"Classic Cars",
      "sortkey":3,
      "productCode":"S12_1108",
      "month":"March",
      "extendedPrice":2280.96,
      "orderDate":"2018-03-17 00:00:00"
   },
   {
      "country":"France",
      "orderNumber":10395,
      "year":2018,
      "countrycode":"fr",
      "quantityOrdered":46,
      "priceEach":123.76,
      "productName":"Diamond T620 Semi-Skirted Tanker",
      "productLine":"Trucks and Buses",
      "sortkey":3,
      "productCode":"S50_1392",
      "month":"March",
      "extendedPrice":5692.96,
      "orderDate":"2018-03-17 00:00:00"
   },
   {
      "country":"France",
      "orderNumber":10395,
      "year":2018,
      "countrycode":"fr",
      "quantityOrdered":45,
      "priceEach":199.49,
      "productName":"1962 City of Detroit Streetcar",
      "productLine":"Trains",
      "sortkey":3,
      "productCode":"S50_1514",
      "month":"March",
      "extendedPrice":8977.05,
      "orderDate":"2018-03-17 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":33,
      "priceEach":185.13,
      "productName":"1969 Ford Falcon",
      "productLine":"Classic Cars",
      "sortkey":3,
      "productCode":"S12_3891",
      "month":"March",
      "extendedPrice":6109.29,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":33,
      "priceEach":159.81,
      "productName":"1903 Ford Model A",
      "productLine":"Vintage Cars",
      "sortkey":3,
      "productCode":"S18_3140",
      "month":"March",
      "extendedPrice":5273.73,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":24,
      "priceEach":89.75,
      "productName":"Collectable Wooden Train",
      "productLine":"Trains",
      "sortkey":3,
      "productCode":"S18_3259",
      "month":"March",
      "extendedPrice":2154,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":45,
      "priceEach":105.32,
      "productName":"1904 Buick Runabout",
      "productLine":"Vintage Cars",
      "sortkey":3,
      "productCode":"S18_4522",
      "month":"March",
      "extendedPrice":4739.4,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":49,
      "priceEach":116.75,
      "productName":"18th century schooner",
      "productLine":"Ships",
      "sortkey":3,
      "productCode":"S24_2011",
      "month":"March",
      "extendedPrice":5720.75,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":27,
      "priceEach":83.2,
      "productName":"1912 Ford Model T Delivery Wagon",
      "productLine":"Vintage Cars",
      "sortkey":3,
      "productCode":"S24_3151",
      "month":"March",
      "extendedPrice":2246.4,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":37,
      "priceEach":90.57,
      "productName":"1940 Ford Delivery Sedan",
      "productLine":"Vintage Cars",
      "sortkey":3,
      "productCode":"S24_3816",
      "month":"March",
      "extendedPrice":3351.09,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"USA",
      "orderNumber":10396,
      "year":2018,
      "countrycode":"us",
      "quantityOrdered":39,
      "priceEach":66.67,
      "productName":"The Schooner Bluenose",
      "productLine":"Ships",
      "sortkey":3,
      "productCode":"S700_1138",
      "month":"March",
      "extendedPrice":2600.13,
      "orderDate":"2018-03-23 00:00:00"
   },
   {
      "country":"France",
      "orderNumber":10397,
      "year":2018,
      "countrycode":"fr",
      "quantityOrdered":32,
      "priceEach":80.55,
      "productName":"The Mayflower",
      "productLine":"Ships",
      "sortkey":3,
      "productCode":"S700_1938",
      "month":"March",
      "extendedPrice":2577.6,
      "orderDate":"2018-03-28 00:00:00"
   }
]

I have created below function but its not working properly

var groups = ['country', 'productLine', 'month']; // this can be dynamic 
var sum = ['priceEach']; // this can be dynamic 

function createGroup (groups, data, sum, childNode) {
    let [primaryGroup, ...rest] = groups;

    let groupedData = data.reduce((acc, current) => {
    let chunk = {
        'Name': current[primaryGroup],
        [primaryGroup]: current[primaryGroup],
        [sum]: data.filter(item => item[primaryGroup] === current[primaryGroup])
        .map(el => el[sum])
        .reduce((total, current) => total + current),
       ...(rest.length > 0 ? {[groups[childNode]]: createGroup(rest, data, sum,childNode+1 )} : {})
    }

    acc.push(chunk)
    return acc
  }, [])
    .reduce((acc, current) => {
        const x = acc.find(item => item[primaryGroup] === current[primaryGroup])
        return !x ? acc.concat([current]) : acc
    }, [])

  return groupedData;
}

const tree = createGroup(groups,data,sum, 1);

Required below sample result (I haven’t included other country but I need all)

[
   {
      "country":[
         {
            "Name":"Spain",
            "priceEach":60.28,
            "productline":[
               {
                  "Name":"Trains",
                  "priceEach":60.28,
                  "month":[
                     {
                        "Name":"March",
                        "priceEach":60.28
                     }
                  ]
               }
            ]
         }
      ]
   },
   {
      "country":[
         {
            "Name":"France",
            "priceEach":578.25,
            "productline":[
               {
                  "Name":"Classic Cars",
                  "priceEach":174.45,
                  "month":[
                     {
                        "Name":"March",
                        "priceEach":174.45
                     }
                  ]
               },
               {
                  "Name":"Trucks and Buses",
                  "priceEach":123.76,
                  "month":[
                     {
                        "Name":"March",
                        "priceEach":123.76
                     }
                  ]
               },
               {
                  "Name":"Trains",
                  "priceEach":199.49,
                  "month":[
                     {
                        "Name":"April",
                        "priceEach":199.49
                     }
                  ]
               },
               {
                  "Name":"Ships",
                  "priceEach":80.55,
                  "month":[
                     {
                        "Name":"June",
                        "priceEach":80.55
                     }
                  ]
               }
            ]
         }
      ]
   }
]

Answer

Well, it is probably hangs because of O(n log n) (you iterate the same data over and over).

Note: priceEach is static in this solution, but you can redesign the recursive function

function createGroup(groups, i, data, parent){
        
    //Take current group    
    var group  = groups[i];
    var nGroup = groups[i+1];
    
    // No more group, stop recursivity
    if (!group) return parent;
    
    // Now, let's reduce by current group
    //FIX: We need to add an empty item to force the reduce method
    if(data.length == 1) {
        data.push({priceEach:0});
    }
    
    var root = data.reduce((prev, next) => {        
        //First time, add prev value
        if(parent.length == 0) {
        
            var item = {};
            item[group] = {
                "Name": prev[group],
                "priceEach": prev.priceEach
            }
            //Next group?
            if(nGroup){
                item[group][nGroup] = createGroup(groups, i+1, [prev], []);
            }
        
            parent.push(item);
        }
    
        //Check if we need to reduce
        for(var item of parent){
            if(item[group].Name == next[group]) {
                item[group].priceEach += next.priceEach;
                //Next group?
                if(nGroup){
                    item[group][nGroup] = createGroup(groups, i+1, [prev, next], item[group][nGroup]);
                }
                return parent;
            }
        }
        
        //We did not reduce, so add next as item
        if(next[group]) {
            var item = {};
            item[group] = {
                "Name": next[group],
                "priceEach": next.priceEach
            }
            //Next group?
            if(nGroup){
                item[group][nGroup] = createGroup(groups, i+1, [next], []);
            }
        
            parent.push(item);
        }

        return parent;
    });
    
    return root;
}

createGroup(["country", "productLine", "month"], 0, data, []);

JSFiddle

If country, productLine, month are static, you can run the classic way