0

I am looking for a solution to group data into weeks / months without external library. I have seen the D3.js nest() option here: how can i group JSON data into the weeks according to calender?

Here another way using npm: https://www.npmjs.com/package/group-by-time

Maybe I am crazy having grown up in the age of dialup, but I still like to keep how many things I load on a page down to what is actually needed instead of loading everything possible, just in case I want to use it, even though the user "might" already have a cached copy from a CDN.

I am currently using Chart.js to display data, also available are Bootstrap and jQuery. I would love to be able to switch the chart between day, week month, using only javascript or jQuery, which seems like it should be a fairly common thing, but I don't see any examples without going for a bigger library like I mentioned above.

var chart={
                dates: ['2015-09-01', '2015-09-02', '2015-09-03', '2015-09-04', '2015-09-05', '2015-09-06', '2015-09-07', '2015-09-08', '2015-09-09', '2015-09-10', '2015-09-11', '2015-09-12', '2015-09-13', '2015-09-14', '2015-09-15', '2015-09-16', '2015-09-17', '2015-09-18', '2015-09-19', '2015-09-20', '2015-09-21', '2015-09-22', '2015-09-23', '2015-09-24', '2015-09-25', '2015-09-26', '2015-09-27', '2015-09-28', '2015-09-29', '2015-09-30', '2015-10-01', '2015-10-02', '2015-10-03', '2015-10-04', '2015-10-05', '2015-10-06', '2015-10-07', '2015-10-08', '2015-10-09', '2015-10-10', '2015-10-11', '2015-10-12', '2015-10-13', '2015-10-14', '2015-10-15', '2015-10-16', '2015-10-17', '2015-10-18', '2015-10-19', '2015-10-20', '2015-10-21', '2015-10-22', '2015-10-23', '2015-10-24', '2015-10-25', '2015-10-26', '2015-10-27', '2015-10-28', '2015-10-29', '2015-10-30', '2015-10-31', '2015-11-01', '2015-11-02', '2015-11-03', '2015-11-04', '2015-11-05', '2015-11-06', '2015-11-07', '2015-11-08', '2015-11-09', '2015-11-10', '2015-11-11', '2015-11-12', '2015-11-13', '2015-11-14', '2015-11-15', '2015-11-16', '2015-11-17', '2015-11-18', '2015-11-19', '2015-11-20', '2015-11-21', '2015-11-22', '2015-11-23', '2015-11-24', '2015-11-25', '2015-11-26', '2015-11-27', '2015-11-28', '2015-11-29', '2015-11-30', '2015-12-01', '2015-12-02', '2015-12-03', '2015-12-04', '2015-12-05', '2015-12-06', '2015-12-07', '2015-12-08', '2015-12-09', '2015-12-10', '2015-12-11', '2015-12-12', '2015-12-13', '2015-12-14', '2015-12-15', '2015-12-16', '2015-12-17', '2015-12-18', '2015-12-19', '2015-12-20', '2015-12-21', '2015-12-22', '2015-12-23', '2015-12-24', '2015-12-25', '2015-12-26', '2015-12-27', '2015-12-28', '2015-12-29', '2015-12-30', '2015-12-31', '2016-01-01', '2016-01-02', '2016-01-03', '2016-01-04', '2016-01-05', '2016-01-06', '2016-01-07', '2016-01-08', '2016-01-09', '2016-01-10', '2016-01-11', '2016-01-12', '2016-01-13', '2016-01-14', '2016-01-15', '2016-01-16', '2016-01-17', '2016-01-18', '2016-01-19', '2016-01-20', '2016-01-21', '2016-01-22', '2016-01-23', '2016-01-24', '2016-01-25', '2016-01-26', '2016-01-27', '2016-01-28', '2016-01-29', '2016-01-30', '2016-01-31', '2016-02-01', '2016-02-02', '2016-02-03', '2016-02-04', '2016-02-05', '2016-02-06', '2016-02-07', '2016-02-08', '2016-02-09', '2016-02-10', '2016-02-11', '2016-02-12', '2016-02-13', '2016-02-14', '2016-02-15', '2016-02-16', '2016-02-17', '2016-02-18', '2016-02-19', '2016-02-20', '2016-02-21', '2016-02-22', '2016-02-23', '2016-02-24', '2016-02-25', '2016-02-26', '2016-02-27', '2016-02-28', '2016-02-29'],
                data: [77.02, 63.80, 21.64, 86.60, 65.40, 46.25, 27.38, 66.65, 67.25, 65.59, 64.80, 01.00, 32.75, 04.30, 51.92, 02.75, 40.20, 72.30, 62.90, 83.60, 66.66, 37.30, 93.90, 01.50, 55.77, 50.00, 73.20, 30.03, 07.95, 21.65, 07.93, 66.94, 11.72, 33.75, 22.80, 14.55, 68.78, 66.78, 52.35, 06.24, 64.78, 22.21, 19.08, 23.69, 54.40, 39.55, 28.76, 22.25, 09.85, 07.50, 22.47, 75.94, 93.34, 16.29, 28.98, 64.40, 78.68, 30.65, 96.65, 99.35, 77.50, 75.30, 89.85, 97.50, 53.90, 97.55, 28.98, 75.08, 25.66, 41.00, 73.72, 68.50, 95.40, 49.50, 32.50, 86.00, 05.43, 88.19, 50.39, 03.90, 82.90, 53.78, 94.20, 82.40, 12.63, 78.80, 07.50, 66.50, 41.75, 91.25, 34.50, 22.50, 85.50, 80.00, 33.75, 59.50, 52.50, 73.25, 76.50, 38.75, 11.00, 55.00, 37.25, 92.50, 74.75, 55.75, 37.25, 61.01, 90.95, 87.65, 99.08, 84.49, 47.00, 72.20, 45.95, 44.35, 10.90, 27.05, 63.10, 63.50, 66.48, 97.25, 39.85, 81.50, 57.90, 02.78, 72.95, 41.55, 39.44, 15.85, 06.60, 13.60, 87.10, 86.40, 90.75, 48.30, 29.75, 16.25, 20.51, 45.40, 51.10, 82.00, 24.24, 92.88, 81.75, 18.50, 89.15, 55.75, 53.50, 74.90, 41.35, 61.40, 07.80, 56.80, 33.75, 75.02, 78.50, 46.10, 39.25, 95.00, 22.85, 36.00, 43.53, 63.80, 32.85, 88.30, 89.69, 05.75, 16.15, 15.25, 20.55, 44.50, 46.28, 71.75, 19.45, 41.75, 25.75, 09.05, 58.77, 76.20, 30.45, 71.75]
            };
Community
  • 1
  • 1
Alan
  • 2,046
  • 2
  • 20
  • 43
  • in your case, does group mean to add the data values? Also, you want to group by weeks of calendar or by 7 days starting from your first date? – juvian Mar 23 '16 at 17:21
  • Thank you for your comment, yes I would like to total the values under data, and I would probably do by calendar of Sunday to Sunday rather than just random groupings of say Friday to Friday. – Alan Mar 23 '16 at 17:28
  • Anything you can find a library for you can probably write yourself, but date manipulation is tricky enough in javascript that if you can find a library that does what you want you should probably just use it. – Dave Mar 23 '16 at 17:34

2 Answers2

2

I'm assuming that date format is YYYY-MM-DD as in sample and for convenience I'm saving both dates and data in different arrays.

var dates = chart.dates;
var data = chart.data;

Keeping above assumptions in mind, you can easily group data by months.

var groupByMonth = {};
dates.forEach(function (d, i) {

    var v = data[i];  
    var monthYear = d.slice(0, 7);    
    if (groupByMonth.hasOwnProperty(monthYear)) {
        groupByMonth[monthYear].push(v);
    } else {
        groupByMonth[monthYear] = [v];
    }
});

console.log(groupByMonth)

However to group by week, you need to find week number. Here is a extension method to do that so:

Date.prototype.getWeekNumber = function(){
    var d = new Date(+this);
    d.setHours(0,0,0);
    d.setDate(d.getDate()+4-(d.getDay()||7));
    return Math.ceil((((d-new Date(d.getFullYear(),0,1))/8.64e7)+1)/7);
};

Using above extension method you can easily group data by week number too. Week number repeats in each year so, I'm using combination of week and year to uniqly identify any week.

var groupByWeek = {};
dates.forEach(function (d, i) {

    var v = data[i];
    var weekYear = d.slice(0, 4) + '-' + new Date(d).getWeekNumber();
    if (groupByWeek.hasOwnProperty(weekYear)) {
        groupByWeek[weekYear].push(v);
    } else {
        groupByWeek[weekYear] = [v];
    }
});

console.log(groupByWeek)
Adnan Umer
  • 3,669
  • 2
  • 18
  • 38
  • 1
    Awesome solution, thank you! I was playing around with this one that also had some promise, but yours is so much shorter and cleaner. http://stackoverflow.com/questions/20630676/how-to-group-objects-with-timestamps-properties-by-day-week-month/20632101#20632101 – Alan Mar 23 '16 at 17:56
1

@Adnan Umer´s solution is probably better for your case since you already have the dates as strings, but here is a more general method in case you had the date instead of string:

function roundDate(date,type){
  var d = new Date(date) // so as to not override

  var types = ["day","week","month"]
  var idx = types.indexOf(type)

  if(idx > -1){ // remove all less than day
     d.setMilliseconds(0)
     d.setSeconds(0)
     d.setMinutes(0)
     d.setHours(0)
     if(idx > 0){ //remove day of the week
         d.setDate(d.getDate() - d.getDay()) // make it a sunday
         if(idx > 1){ //remove day of month
             d.setDate(1) 
         }
     }
  }

  return d;

}

function parseDate(str){ // in your case, we need to keep the date disregarding the locale offset
  return new Date(new Date(str).getTime() + new Date().getTimezoneOffset()*60*1000)
}


var lastDate = null
var grouping = "month"

var datesGrouped = []
var dataGrouped = []

function addToLast(arr,val){return arr[arr.length-1]+=val}

//I am assumming your dates are already sorted

chart.dates.map(function(date,index){
    data = chart.data[index] // get corresponding data point
    var group = roundDate(parseDate(date),grouping).getTime()
    if(group == lastDate){
        addToLast(dataGrouped, data)
    }else{
        datesGrouped.push(group)
        dataGrouped.push(data)
        lastDate = group
    }
})

console.log(datesGrouped, dataGrouped)
juvian
  • 15,875
  • 2
  • 37
  • 38