Reputation: 3
For example, I have an array with plans like this :
[
{ _id: "1", project_id: "1", day: "2021-03-02" },
{ _id: "2", project_id: "1", day: "2021-03-01" },
{ _id: "3", project_id: "1", day: "2021-03-03" },
{ _id: "4", project_id: "2", day: "2021-03-01" },
{ _id: "5", project_id: "1", day: "2021-03-04" },
{ _id: "6", project_id: "1", day: "2021-03-10" }
]
I need to compute the number of consecutive days (no weekends) for plans that have the same project id. For the example, the result needs to be :
[
{ _id: "1", project_id: "1", day: "2021-03-02", count: 4 },
{ _id: "2", project_id: "1", day: "2021-03-01", count: 4 },
{ _id: "3", project_id: "1", day: "2021-03-03", count: 4 },
{ _id: "4", project_id: "2", day: "2021-03-01", count: 1 },
{ _id: "5", project_id: "1", day: "2021-03-04", count: 4 },
{ _id: "6", project_id: "1", day: "2021-03-10", count: 1 }
]
I tried something like the following, but it returns the same count in every plan with the same project :
let plans = [
{ _id: "1", project_id: "1", day: "2021-03-02" },
{ _id: "2", project_id: "1", day: "2021-03-01" },
{ _id: "3", project_id: "1", day: "2021-03-03" },
{ _id: "4", project_id: "2", day: "2021-03-01" },
{ _id: "5", project_id: "1", day: "2021-03-04" },
{ _id: "6", project_id: "1", day: "2021-03-10" }
];
plans.filter(p => new Date(p.day).getDay() !== 6 && new Date(p.day).getDay() !== 0).map((plan, i, arr) => {
let count = 1;
arr
.filter(p2 => plan.project_id === p2.project_id)
.sort((a, b) => new Date(a.day).getTime() - new Date(b.day).getTime())
.map((plan3, k, arr3) => {
if (k > 0) {
let tmpDate = new Date(arr3[k - 1].day);
if (tmpDate.getDay() === 5) {
tmpDate.setDate(tmpDate.getDate() + 3);
} else {
tmpDate.setDate(tmpDate.getDate() + 1);
}
if (tmpDate.getTime() === new Date(plan3.day).getTime()) {
count++;
}
}
});
plan.count = count;
})
console.log(plans);
Upvotes: 0
Views: 197
Reputation: 350345
The problem is that when there is a gap, you do not create a new series. You do test that condition with this:
if (tmpDate.getTime() === new Date(plan3.day).getTime()) {
count++;
}
... but you are not saying that a plan after a gap does not belong to the same series, ... you just do not count it in the count. But that count will still be applied to that plan. You should really treat a gap like you treat a switch of project.
It is also a pity that you have to redo the same analysis for all days that belong to the same project. The outcome will be the same every time, so that is a loss of efficiency. This can be done with one loop, without the need of a nested loop.
Below is how it could be done. Admittedly there is a nested loop (with .slice....forEach
), but that is only over a distinct slice of the data. If you count all those iterations together, you'll have done n iterations in total for that inner loop. So that does not make it quadratic. Each plan is only once the subject of that inner loop.
function nextWorkingDay(a) {
a = new Date(a); // clone
let d = a.getDay();
a.setDate(a.getDate() + (d < 5 ? 1 : 8 - d));
return a;
}
function addCounts(data) {
let sorted = [...data].sort((a,b) =>
a.project_id.localeCompare(b.project_id) || a.day.localeCompare(b.day)
);
let count = 0;
sorted.forEach((o, i, arr) => {
count++;
if (i === arr.length - 1 || arr[i+1].project_id != o.project_id
|| +nextWorkingDay(o.day) !== Date.parse(arr[i+1].day)) {
// Note how a break in consecutive days is treated in the same
// way as a break in the project_id
arr.slice(i - count + 1, i + 1).forEach(o => o.count = count);
count = 0;
}
});
}
// Sample data
let data = [
{ _id: "1", project_id: "1", day: "2021-03-02" },
{ _id: "2", project_id: "1", day: "2021-03-01" },
{ _id: "3", project_id: "1", day: "2021-03-03" },
{ _id: "4", project_id: "2", day: "2021-03-01" },
{ _id: "5", project_id: "1", day: "2021-03-04" },
{ _id: "6", project_id: "1", day: "2021-03-10" }
];
addCounts(data);
console.log(data);
Upvotes: 1