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.
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.
Share Improve this question asked Dec 17, 2013 at 9:42 Running TurtleRunning Turtle 12.7k20 gold badges57 silver badges81 bronze badges 5 |7 Answers
Reset to default 53All 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.
Here is my solution. Keep in mind that day, week and month are relative to origin, since epoch:
let event = [ {id: 1, date: 1387271989749 }, {id:2, date: 1387271989760} ];
function groupday(value, index, array){
let byday={};
let d = new Date(value['date']);
d = Math.floor(d.getTime()/(1000*60*60*24));
byday[d]=byday[d]||[];
byday[d].push(value);
return byday
}
function groupweek(value, index, array){
let byweek={};
let d = new Date(value['date']);
d = Math.floor(d.getTime()/(1000*60*60*24*7));
byweek[d]=byweek[d]||[];
byweek[d].push(value);
return byweek
}
function groupmonth(value, index, array){
let bymonth={};
d = new Date(value['date']);
d = (d.getFullYear()-1970)*12 + d.getMonth();
bymonth[d]=bymonth[d]||[];
bymonth[d].push(value);
return bymonth
}
console.log('grouped by day', event.map(groupday));
console.log('grouped by week',event.map(groupweek));
console.log('grouped by month',event.map(groupmonth));
NOTE: Objects are grouped but the keys for day, week and month are in digits. Only the key for year is human readable.
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 1970objPeriodWeek
: the week number relative to the first week of 1st Jan 1970objPeriodMonth
: the month number since 1st Jan 1970objPeriodYear
: 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".
I would do something like this :
var item,
i = 0,
groups = {},
year, day;
while (item = eventsArray[i++]) {
item = new Date(item.date);
year = item.getFullYear();
day = item.getDate();
groups[year] || (groups[year] = {}); // exists OR create {}
groups[year][day] || (groups[year][day] = []); // exists OR create []
groups[year][day].push(item);
}
This version groups items by days only, but you can easily get the same result for weeks and months replacing item.getDate()
with the appropriate function :
getWeek()
: https://stackoverflow.com/a/6117889/1636522.getMonth()
: MDN doc (zero based!).
Another implementation using dayjs library
var dayjs = require('dayjs')
function groupTimesBy(theList, unit = 'day', timeParamName = 'create_time'){
var toReturn = {};
for(let listItem of theList) {
const paramName = dayjs.unix(listItem[timeParamName]).startOf(unit).unix();
if(toReturn[paramName] == null) {
toReturn[paramName] = [];
}
toReturn[paramName].push(listItem);
}
return toReturn;
}
let list = [{'time':1609104570}, {'time':1609104570}];
let byDay = groupTimesBy(list, 'day', 'time');
let byWeek = groupTimesBy(list, 'week', 'time');
let byMonth = groupTimesBy(list, 'month', 'time');
let byYear = groupTimesBy(list, 'year', 'time');
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);
}
Modified @user568109's answer to use reduce and take a single argument
function groupByDay(events) {
return events.reduce((byDay, event) => {
let dayNumber = Math.floor(new Date(event.date).getTime() / (1000 * 60 * 60 * 24));
byDay[dayNumber] = byDay[dayNumber] || [];
byDay[dayNumber].push(event);
return byDay;
}, {});
}
function groupByWeek(events) {
return events.reduce((byWeek, event) => {
let weekNumber = Math.floor(new Date(event.date).getTime() / (1000 * 60 * 60 * 24 * 7));
byWeek[weekNumber] = byWeek[weekNumber] || [];
byWeek[weekNumber].push(event);
return byWeek;
}, {});
}
Date
to extract the date and then sorting accordingly? – Manishearth Commented Dec 17, 2013 at 9:45moment(1387271989749).week()
. – punund Commented Dec 17, 2013 at 9:49