In a nodejs application, I have an array of event objects formatted as follows:
eventsArray = [ {id: 1, date: 1387271989749 }, {id:2, date: 1387271989760}, ... ]
eventsArray having a variable length of n elements, and supposing I choose time reference to be Paris time, I want to be able to group elements by day, week, or month:
groupedByDay = {
2012: { ... },
2013: {
dayN : [{id: a0, date: b0}, {id: a1, date: b1}, {id: a2, date: b2 }],
dayN+1: [{id: a3, date: b3}, {id: a4, date: b4}, {id: a5, date: b5 }],
...
},
2014: { ... }
}
groupedByWeek = {
2012: { ... }
2013: {
weekN: [{id: a0, date: b0}, {id: a1, date: b1}, {id: a2, date: b2 }],
weekN+1: [{id: a3, date: b3}],
....
},
2014: { ... }
}
groupedByMonth = {
2012: { ... },
2013: {
monthN: [ {id: a0, date: b0 }, {id: a1, b1}, {id: a2, b2}, {id: a3, b3 }],
monthN+1: [ {id: a4, date: b4 }, {id: a5, b5}, {id: a6, b6}],
...
},
2014: { ... }
}
Having very little experience with manipulating unix timestamps, I was wondering how this could be done or if there was an npm module that would make this easier.
All the solutions above are hefty, pure JS, vanilla solutions. If you're okay to use a couple of libraries, then lodash and moment can be used together for a simple one liner:
ES6
let groupedResults = _.groupBy(results, (result) => moment(result['Date'], 'DD/MM/YYYY').startOf('isoWeek'));
Older JS
var groupedResults = _.groupBy(results, function (result) {
return moment(result['Date'], 'DD/MM/YYYY').startOf('isoWeek');
});
This will result in an array that's keyed by the start of the week, such Mon Jul 25 2016 00:00:00 GMT+0100
. I'm sure you can work out how to expand on it to get months, years, etc.
RE: @SaintScott's comments
It was mentioned in the comments that this doesn't directly answer the question because the original uses UTC timestamps rather than formatted dates. In this case, you should use moment()
without the second parameter:
moment(1387271989749).startOf('isoWeek');
Or if using a UNIX timestamp, as follows:
moment.unix(yourTimestamp).startOf('isoWeek');
... although this is starting to get further from the question and more into the Moment documentation, which I'd advise reading if you want to use this method.
Expanding on @user568109's answer, which is correct and up-voted, here's one function that does it all, with a bigger array for demonstration's sake:
// Group by time period - By 'day' | 'week' | 'month' | 'year'
// ------------------------------------------------------------
var groupByTimePeriod = function (obj, timestamp, period) {
var objPeriod = {};
var oneDay = 24 * 60 * 60 * 1000; // hours * minutes * seconds * milliseconds
for (var i = 0; i < obj.length; i++) {
var d = new Date(obj[i][timestamp] * 1000);
if (period == 'day') {
d = Math.floor(d.getTime() / oneDay);
} else if (period == 'week') {
d = Math.floor(d.getTime() / (oneDay * 7));
} else if (period == 'month') {
d = (d.getFullYear() - 1970) * 12 + d.getMonth();
} else if (period == 'year') {
d = d.getFullYear();
} else {
console.log('groupByTimePeriod: You have to set a period! day | week | month | year');
}
// define object key
objPeriod[d] = objPeriod[d] || [];
objPeriod[d].push(obj[i]);
}
return objPeriod;
};
var eventsArray = [{ id: 1, date: 1317906596 }, { id: 2, date: 1317908605 }, { id: 3, date: 1317909229 }, { id: 4, date: 1317909478 }, { id: 5, date: 1317909832 }, { id: 6, date: 1317979141 }, { id: 7, date: 1317979232 }, { id: 8, date: 1317986965 }, { id: 9, date: 1318582119 }, { id: 10, date: 1318595862 }, { id: 11, date: 1318849982 }, { id: 12, date: 1318855706 }, { id: 13, date: 1318929018 }, { id: 14, date: 1318933265 }, { id: 15, date: 1318940511 }, { id: 16, date: 1318945096 }, { id: 17, date: 1319017541 }, { id: 18, date: 1319527136 }, { id: 19, date: 1318582119 }, { id: 20, date: 1318595862 }, { id: 21, date: 1318582119 }, { id: 22, date: 1318595862 }, { id: 23, date: 1319713399 }, { id: 24, date: 1320053428 }, { id: 25, date: 1320333481 }, { id: 26, date: 1320832755 }, { id: 27, date: 1321012378 }, { id: 28, date: 1321280993 }, { id: 29, date: 1321347659 }, { id: 30, date: 1321350476 }, { id: 31, date: 1321369307 }, { id: 32, date: 1321369614 }, { id: 33, date: 1321610123 }, { id: 34, date: 1321613205 }, { id: 35, date: 1321617250 }, { id: 36, date: 1321626603 }, { id: 37, date: 1321865808 }, { id: 38, date: 1321876609 }, { id: 39, date: 1321877598 }, { id: 40, date: 1321877832 }, { id: 41, date: 1321953322 }, { id: 42, date: 1322061969 }, { id: 43, date: 1322142603 }, { id: 44, date: 1322211686 }, { id: 45, date: 1322213793 }, { id: 46, date: 1322214569 }, { id: 47, date: 1322482817 }, { id: 48, date: 1322663742 }, { id: 49, date: 1322664267 }, { id: 50, date: 1322747231 }, { id: 51, date: 1322819964 }, { id: 52, date: 1323358224 }, { id: 53, date: 1323681272 }, { id: 54, date: 1323695093 }, { id: 55, date: 1323696589 }, { id: 56, date: 1323763763 }, { id: 57, date: 1322819964 }, { id: 58, date: 1323681272 }, { id: 59, date: 1323851164 }, { id: 60, date: 1323853123 }, { id: 61, date: 1323854271 }, { id: 62, date: 1323858072 }, { id: 63, date: 1325690573 }, { id: 64, date: 1325751893 }, { id: 65, date: 1325760204 }, { id: 66, date: 1325769098 }, { id: 67, date: 1325769981 }, { id: 68, date: 1325771632 }, { id: 69, date: 1325776473 }, { id: 70, date: 1325837346 }, { id: 71, date: 1326110199 }, { id: 72, date: 1326793097 }, { id: 73, date: 1326878182 }, { id: 74, date: 1326881341 }, { id: 75, date: 1326975873 }, { id: 76, date: 1326985667 }, { id: 77, date: 1327047585 }, { id: 78, date: 1327062945 }, { id: 79, date: 1327063660 }, { id: 80, date: 1327322844 }, { id: 81, date: 1327326904 }, { id: 82, date: 1327329215 }, { id: 83, date: 1327397042 }, { id: 84, date: 1327399839 }, { id: 85, date: 1327401818 }, { id: 86, date: 1327407161 }, { id: 87, date: 1327419420 }, { id: 88, date: 1327570243 }, { id: 89, date: 1327578536 }, { id: 90, date: 1327584554 }, { id: 91, date: 1327914616 }, { id: 92, date: 1327917019 }, { id: 93, date: 1327931685 }, { id: 94, date: 1327933025 }, { id: 95, date: 1327934772 }, { id: 96, date: 1327947074 }, { id: 97, date: 1328626734 }, { id: 98, date: 1328626734 }, { id: 99, date: 1330070074 }, { id: 100, date: 1330073135 }, { id: 101, date: 1330073259 }, { id: 102, date: 1330332445 }, { id: 103, date: 1330351925 }, { id: 104, date: 1330420928 }, { id: 105, date: 1330423209 }, { id: 106, date: 1330437337 }, { id: 107, date: 1330439446 }];
var objPeriodDay = groupByTimePeriod(eventsArray, 'date', 'day');
var objPeriodWeek = groupByTimePeriod(eventsArray, 'date', 'week');
var objPeriodMonth = groupByTimePeriod(eventsArray, 'date', 'month');
var objPeriodYear = groupByTimePeriod(eventsArray, 'date', 'year');
console.log(objPeriodDay);
console.log(objPeriodWeek);
console.log(objPeriodMonth);
console.log(objPeriodYear);
And here's a fiddle to go with it (you will have to open the console to see the output).
As you'll see in the fiddle, the keys for the 4 objects will be as follows:
objPeriodDay
: the day number since 1st Jan 1970
objPeriodWeek
: the week number relative to the first week of 1st Jan 1970
objPeriodMonth
: the month number since 1st Jan 1970
objPeriodYear
: the year
Note that all these four variations will give you unique keys. Month, for instance, will give you "Dec 2011" (in the form of months since 1st Jan 1970), not just "Dec".
Something like this is probably close to what you need.
Javascript Date has getFullYear
, getDate
and getMonth
functions (https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) and you can also check out this SO post (Convert a Unix timestamp to time in JavaScript).
The basic method here (arrange) builds up the hashes by year, and then by day and month.
This code doesn't do week. Do you mean which week of the year or which month? I suspect you could write your own date method to grab that number and then follow the pattern below to grab all the data you want. You can get the day of the week from JS (getDay
). I'm not sure how you want to compute week, but that method might help out.
I ran this code in a browser (after initializing a dummy set of eventsArray) but I suspect it'd translate to node just fine.
You should properly namespace the function and you could move all the methods into the object prototype, if you were so inclined.
Hope this helps
var groupEvents = function(eventsArray) {
this.dates = eventsArray;
this.arranged = {};
this.monthKey = function(month) {
return 'month' + month;
};
this.dayKey = function(month) {
return 'day' + month;
};
this.getYear = function(year) {
this.arranged[year] = this.arranged[year] || {}
return this.arranged[year]
};
this.getMonth = function(month, year) {
var y = this.getYear(year);
var k = this.monthKey(month);
y[k] = y[k] || [];
return y[k]
};
this.getDate = function(day, year) {
var y = this.getYear(year);
var k = this.dayKey(day);
y[k] = y[k] || [];
return y[k]
};
this.addToMonth = function(info, month, year) {
var y = this.getMonth(month,year);
y.push(info);
};
this.addToDay = function(info, day, year) {
var y = this.getDate(day,year);
y.push(info);
};
this.breakdownDate = function(date) {
return {
month: date.getMonth(),
day: date.getDate(),
year: date.getFullYear()
};
}
/** grab a date, break it up into day, month year
and then categorize it */
this.arrange = function() {
if(!this.arranged.length) {
var ii = 0;
var nn = this.dates.length;
for(; ii < nn; ++ii ) {
var el = this.dates[ii];
var date = new Date(el.date * 1000);
var parsed = this.breakdownDate(date);
this.addToMonth(el, parsed.month, parsed.year);
this.addToDay(el, parsed.month, parsed.year);
}
}
return this.arranged;
};
return this;
};
if(eventArray.length) {
var events = new groupEvents(eventArray);
var arranged = events.arrange();
console.log(arranged);
}