DEV Community

Rick Delpo
Rick Delpo

Posted on

group by month and sum values for each month explained in plain english and done in plain javascript with example

Instead of using lodash and moment.js we can group and sum in plain javascript using the Date object, indexOf method and array.reduce. Why use moment.js when we only need a few lines of code to accomplish the same in plain js.

Use case - we often need time series data in Data Analytics and Visualizations. For example, a bar chart showing sales per month of each product line.

In order to achieve group like behavior on timestamps we use logic that says while our month name is the current month sum all values for current mo. When month name changes start all over again. For summing vals we use the array.reduce method. For grouping we keep track of IndexOf date at each pass of our original array.

Solution - we grab the month name from an array of timestamps by using the getMonth method of the javascript Date object. We need to keep track of when the month changes so we establish an empty control array and push in month name upon its change. The indexOf date will be -1 when this happens because new month is not in our array yet so there is no match found. The indexOf method is used to return the index found at each pass.

I am now getting into the weeds so let me show u an example.

  // Because JS doesn't have a nice way to name months
var monthNames = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
var result = [];   //transformed array after below operations are performed
var dateArr = [];   //to keep track of month name and when it changes
let initialValue = 0; //need to pass in an initial value or reduce omits first element of array

var dataByMonth= original_array.reduce(function(prev, currValue){ 

date = new Date(currValue['order_date']); 
date = monthNames[date.getMonth()]; //gets month names
           //next index of is acting like a find function
var index = dateArr.indexOf(date); //equaling -1 on first pass 
 if (index == -1) {  //is equal to -1 only for first pass, above forces -1 so we can initialize first pass
       dateArr.push(date);  //no match for month name until it is pushed..dateArr starts as empty until mo val is pushed
     var obj = {month: date, product1: currValue.product1, product2: currValue.product2, product3: currValue.product3};
       result.push(obj); //push in default vals for new mo
   } 
   else {
    //index = 0 because dateArr has val now...then for next mo index = 1
               //on subsequent passes index will not be -1 so just increment cur vals until new mo is encountered
  result[index].product1 += currValue.product1; //increment sum each pass starting index =0
  result[index].product2 += currValue.product2;   //no need to push, result val updates each time
  result[index].product3 += currValue.product3;
   }
return prev +currValue; //need to return both prev and curr, for first element to register ??
} //end of reduce

, initialValue);  //initial value here, this is the 2nd arg of array.reduce, first arg is callback function above
console.log(JSON.stringify(result, null, 1));


Enter fullscreen mode Exit fullscreen mode

Click below for the full code that I am operating on. Be sure timestamps are in valid date format yyyy-mm-dd otherwise I do have a quickie conversion method shown in the full code. PS press ctrl, shift,j to show each iteration logged onto console.

Click here for an example of my stacked bar chart
https://dev.to/rickdelpo1/how-to-populate-a-stacked-bar-chart-in-plain-javascript-12p9

Happy Coding !!

Top comments (0)