Last Updated: September 27, 2021
·
60.89K
· namuol

Remove items from Array while iterating over it.

Ever encounter unexpected results when items are removed from an array while the array is being iterated over? This might look familiar:

var numbers = [17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16],
    i;

for (i=0; i < numbers.length; ++i) {
  if (numbers[i] % 2 === 0) {
    numbers.splice(i, 1); // Remove even numbers
  }
}

console.log(numbers); // [17, 14, 15, 8, 7, 1, 9, 19, 3, 5, 6]
                      // WTF: ^       ^                     ^

What's going on here? You'll notice that the even numbers that didn't get removed were each preceded by another even number.

After splice is called, the item at index i is now what was the next item in the list, then the for loop triggers ++i, which skips to the next next item, leaving
an item unchecked. Watch carefully:

[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16] // i = 0
 ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16] // i = 1
     X
[17, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]    // i = 2; skipped `14`
         ^
[17, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]    // i = 3
             X
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 4; skipped `8`
                ^
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 5
                   ^
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 6
                      ^
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 7
                         ^
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 8
                             ^
[17, 14, 15, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16]        // i = 9
                                X
[17, 14, 15, 8, 7, 1, 9, 19, 3, 5, 4, 6, 16]            // i = 10; skipped `5`
                                   X
[17, 14, 15, 8, 7, 1, 9, 19, 3, 5, 6, 16]               // i = 11; skipped `6`
                                      X
[17, 14, 15, 8, 7, 1, 9, 19, 3, 5, 6]                   // End result.

The most "obvious" solution here is to simply decrement i whenever an item is removed:

for (i=0; i < numbers.length; ++i) {
  if (numbers[i] % 2 === 0) {
    numbers.splice(i, 1); // Remove even numbers
    --i; // Correct the index value
  }
}

But we can do better. Simply by walking through the array in reverse, we avoid the issue entirely.

for (i = numbers.length - 1; i >= 0; --i) {
  if (numbers[i] % 2 === 0) {
    numbers.splice(i, 1); // Remove even numbers
  }
}

Now, whenever an item is removed, i automatically "falls" to the correct item. Watch:

[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6, 16] // i = 15
                                                    X
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4, 6]     // i = 14
                                                 X
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5, 4]        // i = 13
                                              X
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5]           // i = 12
                                           ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 18, 5]           // i = 11
                                       X
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 10
                                    ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 9
                                ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 8
                             ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 7
                          ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 6
                       ^
[17, 2, 14, 15, 20, 8, 7, 1, 9, 19, 3, 5]               // i = 5
                    X
[17, 2, 14, 15, 20, 7, 1, 9, 19, 3, 5]                  // i = 4
                X
[17, 2, 14, 15, 7, 1, 9, 19, 3, 5]                      // i = 3
            ^
[17, 2, 14, 15, 7, 1, 9, 19, 3, 5]                      // i = 2
        X
[17, 2, 15, 7, 1, 9, 19, 3, 5]                          // i = 1
     X
[17, 15, 7, 1, 9, 19, 3, 5]                             // i = 0; done!
 ^