You have the exact same performance characteristics with both method 1 and method 2, however, your way of measuring them is flawed. With method 2, you count the outer and inner iterations made by the code, while with method 1, you only count the outer iterations. However, .some()
still does an iteration over the data:
// sample data
const monthDays = Array.from({ length: 30 }, (v, k) => ({ date: k + 1, disabled: false }));
const disabledDates = Array.from({ length: 30 }, (v, k) => k + 1);
//set disabled dates
// method 1
let counter = 0;
for (let day of monthDays) {
day.disabled = disabledDates.some(d => {
counter++;
return d === day.date
});
counter++;
}
console.log(counter) // logs 495
See also on TypeScript Playground
Since you have two nested iterations, both methods exhibit an O(n*m)
time complexity, where n
is the length of monthDays
and m
is the length of disabledDates
. This is basically quadratic complexity for close values of n
and m
and actually quadratic for n = m
(as is the example).
The way to correct that is to eliminate the nested loops and only process the data once. This is easily achievable by first pre-computing a Set
object that contains all disabledDates
, that way you don't have to loop over an array only to check if they exist instead using Set#has
.
// sample data
const monthDays = Array.from({ length: 30 }, (v, k) => ({ date: k + 1, disabled: false }));
const disabledDates = new Set(Array.from({ length: 30 }, (v, k) => k + 1));
for (const day of monthDays) {
day.disabled = disabledDates.has(day.date);
}
console.log(monthDays);
See also on TypeScript Playground
The one time conversion to a set has a complexity of O(m)
and then each time you loop over you only have an O(n)
iteration.
- If you can create set once only, then repeatedly iterating
monthDays
don't need to include that operation. Thus the complexity of the operation becomes O(n)
.
- If you need to create a set every time before you loop, then the complexity becomes an
O(n+m)
which is still better than quadratic and essentially linear.
As a note here, this assumes that .has()
is a O(1)
operation. This is a reasonable assumption that probably holds true most of the time. However, technically, that's not guaranteed - the specifications define a sub-linear complexity on set operations, so you might have O(log n)
complexity, which means that the iteration over monthDays
and looking up disabledDates
will be O(n * log m)
. Even then, using a set is probably the easiest optimisation path. If performance is still a problem, then perhaps generating an object as a lookup table could be better:
//this should be generated
const lookup: Record<number, true> = {
1: true,
2: true,
3: true,
/*...*/
}
/*...*/
//fetch from lookup or set `false` if not in the lookup
const disabled: boolean = lookup[day.date] ?? false;