I'd like to have array iterator that skips gaps (like forEach
), and visits elements added during iteration (unlike forEach
).
Pseudo-implementation:
Array.prototype.sparseEntries = function* sparseEntries() {
for (const [i, v] of this.entries()) {
if (i in this) {
yield [i, v];
}
}
}
Example:
function f(v, i, a) {
console.log(`${i}: ${v}`);
if (v < 10) a[2 * i] = 3 * v;
}
> [,,3].forEach(f);
2: 3
> { let a = [,,3]; for (let [i, v] of a.sparseEntries()) f(v, i, a); }
2: 3
4: 9
8: 27
1 Like
Is that the new way to write
if (i in this) {
?
Do you think adding this to the set of built in methods would encourage people to use sparse arrays more often?
Does keeping these helpers as independent libraries, e.g on npm, cause a significant issue?
Thank you, that does exactly what I wanted.
Not as a primary goal. Rather, starting from a point where you already have (or consider to use) a sparse array, facilitating usage in for loops.
Significant, no. A library function filtering indices would add a little more overhead to an already slow operation. I'd probably keep checking if (idx in ary)
by hand.
// forEach
pnpx benchmark -e 'a = []; a[1e6] = 1; a.forEach((v, i) => out = v);'
✔ eval-1 x 31.30 ops/sec ±0.16% (54 runs sampled)
// for...of
$ pnpx benchmark -e 'a = []; a[1e6] = 1; for (let [i, v] of a.entries()) out = v;'
✔ eval-1 x 21.93 ops/sec ±0.52% (39 runs sampled)
// for...of + if...in
$ pnpx benchmark -e 'a = []; a[1e6] = 1; for (let [i, v] of a.entries()) if (i in a) out = v;'
✔ eval-1 x 12.89 ops/sec ±1.02% (35 runs sampled)
Note: you do want to be careful to make sure i
is a number - that will also return true
for length
as well as any other prototype method. (I would personally still recommend using hasOwnProperty
to ensure that length
is the only exception, for security reasons if nothing else.)