Problem
The question is very simple : I have an array of items, that all have a date. According to the date, I would like to split this array into several arrays.
In my case, I would like to split the array into 3 arrays : one that contains result of the last 24h, the other 48h, then the last one on the week.
Here is a minimal reproduction of the case, with the final solution.
const now = Date.now();
const day = 1000 * 3600 * 24;
const todayTime = now - day;
const yesterdayTime = now - day * 2;
const weekTime = now - day * 7;
const arr = [
{ id: 1, date: todayTime + 100 },
{ id: 2, date: yesterdayTime + 100 },
{ id: 3, date: weekTime + 100 },
];
const today = arr.filter(item => item.date - todayTime > 0);
const yesterday = arr.filter(item => item.date - yesterdayTime > 0 && item.date - todayTime < 0);
const week = arr.filter(item => item.date - weekTime > 0 && item.date - yesterdayTime < 0);
console.log(today, yesterday, week);
Is there a way to make it more concise ? I’m talking about code length : I would like to reduce it to the maximum, especially the definition of the 3 arrays. I tried with reduce, but the syntax is pretty ugly.
Any ideas ?
Solution
Always create a function
Its not really code if its not a function, you may as well just output the 3 arrays directly. (my personal view on global inline code).
You may say, “This is just an example”. No it can not be just an example, a function has special powers and is written differently than inline code. Plus examples are boarder line off topic here, this question may get closed.
Part of a functions power is that it makes you think about how you solve the problem differently from inline code.
Look for change
When you write a function you look for the parts of the logic and data that change. You pass that to the function as arguments. The function uses these argument to process the data and return the desired results.
In this case you have
- the array of items to split.
- The number of days old to split the data, eg 1,2,and 7 days.
Thus we can have something like the following. The function is declared inside another so that all it needs is safely encapsulated and outside of unrelated scopes.
const splitByDaysOld = (() => {
const MS_IN_DAY = 8.64e7;
const sorter = (a, b) => a - b;
return function(array, periods) {
const now = Date.now();
var start = 0;
return periods.sort(sorter).map(day => {
const res = array.filter(item => {
const daysOld = (item.date - now) / MS_IN_DAY;
return daysOld >= start && daysOld < day;
});
start = day;
return res;
});
};
})();
It returns an array of arrays, one for each period.
Example below has 3 periods 0 to < 1, 1 to < 2, and 2 to < 7 days old.
const [today, yesterday, week] = splitByDaysOld(arr, [1, 2, 7]);
Note that the periods array is sorted from most recent to oldest and that the result will be in the same order
item.date - todayTime > 0
is the same as item.date > todayTime.
Your less-than condition should be less-than-or-equal, otherwise anything falling on a day boundary won’t match any category.
const now = Date.now(),
day = 1000 * 3600 * 24,
arr = [
{ id: 1, date: now - day + 100 },
{ id: 2, date: now - day*2 + 100 },
{ id: 3, date: now - day*7 + 100 },
],
daysAgo=[ 1, 2, 7 ],
filtered=daysAgo.map(
(days,i) => arr.filter(
j => j.date > now-day*days && j.date <= now-day*( i ? daysAgo[i-1] : -1 )
)
);
console.log(filtered);