Reputation: 1488
I'm wondering if and what is a reliable and/or standard way of iterating an array whose length is changing inside the loop. I ask because I end up choosing a different method to do this each time I want to do it, e.g.
for ( var i = 0; i < myarray.length; i++ ) {
if (myarray[i] === 'something') {
myarray.splice(i, 1);
// *to avoid jumping over an element whose index was just shifted back to the current i
i--;
}
}
or
var i = 0;
while (myarray[i]) {
if (myarray[i] === 'something') {
myarray.splice(i, 1);
} else {
i++;
}
}
These are the ways I find myself doing this, but I'm curious if there is a standard approach.
Upvotes: 9
Views: 7230
Reputation: 382454
I find simpler to iterate in the other direction :
for (var i=myarray.length; i--; ) {
if (myarray[i] === 'something') myarray.splice(i, 1);
}
This way you don't have to change the increment when removing.
Many developers, especially the ones who didn't deal with C-like languages before JavaScript, find it confusing to deal with the subtleties of the decrement operator. The loop I wrote can also be written as
for (var i=myarray.length-1; i>=0; i--) {
Upvotes: 23
Reputation: 23502
However you choose to do it, starting in reverse and counting down is simplest. It also depends on whether your array is sparse and if you wish for it to remain sparse. Easiest is to create yourself a reusable function and your own library. You could do this. If you set compress
to true then your array will become a continuous rather than sparse array. This function will remove all matched occurrences of the value and will return an array of the removed elements.
Javascript
function is(x, y) {
if (x === y) {
if (x === 0) {
return 1 / x === 1 / y;
}
return true;
}
var x1 = x,
y1 = y;
return x !== x1 && y !== y1;
}
function removeMatching(array, value /*, compress (default = false)*/ ) {
var removed = [],
compress = arguments[2],
index,
temp,
length;
if (typeof compress !== "boolean") {
compress = false;
}
if (compress) {
temp = [];
length = array.length;
index = 0;
while (index < length) {
if (array.hasOwnProperty(index)) {
temp.push(array[index]);
}
index += 1;
}
} else {
temp = array;
}
index = 0;
length = temp.length;
while (index < length) {
if (temp.hasOwnProperty(index) && is(temp[index], value)) {
if (compress) {
removed.push(temp.splice(index, 1)[0]);
} else {
removed.push(temp[index]);
delete temp[index];
}
}
index += 1;
}
if (compress) {
array.length = 0;
index = 0;
length = temp.length;
while (index < length) {
if (temp.hasOwnProperty(index)) {
array.push(temp[index]);
}
index += 1;
}
}
return removed;
}
var test = [];
test[1] = 1;
test[50] = 2;
test[100] = NaN;
test[101] = NaN;
test[102] = NaN;
test[200] = null;
test[300] = undefined;
test[400] = Infinity;
test[450] = NaN;
test[500] = -Infinity;
test[1000] = 3;
console.log(test);
console.log(removeMatching(test, NaN));
console.log(test);
console.log(removeMatching(test, Infinity, true));
console.log(test);
Output
[1: 1, 50: 2, 100: NaN, 101: NaN, 102: NaN, 200: null, 300: undefined, 400: Infinity, 450: NaN, 500: -Infinity, 1000: 3]
[NaN, NaN, NaN, NaN]
[1: 1, 50: 2, 200: null, 300: undefined, 400: Infinity, 500: -Infinity, 1000: 3]
[Infinity]
[1, 2, null, undefined, -Infinity, 3]
On jsfiddle
Upvotes: 0