Performance testing a for loop vs. .indexOf and splice vs. .filter
I demonstrate how to use .indexOf and .splice to remove an item from an array. Then I compare performance with a for loop and the .filter method.
Photo by Jackson Simmer on Unsplash
In other words, the fastest way to modify the array to no longer include a specific value.
Removing an item from an Array
One way to solve this problem is using Array.prototype.indexOf() to find the index of the value, then Array.prototype.splice() to remove that item:
Note that .indexOf returns -1 if the index is not found, but .splice interprets an index of -1 as the last item in the array, just like .slice.
The following code using the ? question mark operator is equivalent:
You could also write a one-liner if you really don?t mind the performance hit of searching the entire array twice if the value is found:
All of these methods only remove the first instance of the given value:
I?ll discuss options for removing all matching items later in this article.
What about a for loop?
A perfectly good option to remove an item from an array would be to use a for loop, though it could potentially be harder to read in your code:
To make it equivalent to using .indexOf and only remove the first instance, I used a variable to keep track of whether the value had been removed.
The following would remove all instances using .indexOf and .splice:
And the following would remove all instances using a for loop:
Which is faster?
Performance testing using these jsPerf test cases shows a big difference between the two methods of removing an item from an array:
Using a for loop appears 2.5x faster than .indexOf and .splice.
This difference is magnified when removing all instances of the matching value, which I tested in these jsPerf test cases:
As you can see, the for loop appears 5x faster compared to the while loop using .indexOf and .splice in a really inefficient manner.
But these results are misleading ? because the processor is still crunching 4000 operations each millisecond (4,000,000 ops/sec).
As you will see later, .indexOf and .splice actually have better performance than the for loop when dealing with large arrays of 10,000 items.
What about .filter?
Here is an example of removing all items from an array using .filter, which returns a filtered array of the values that match the given conditional:
On the plus side, filter results in much less code. But how fast is it?
In these jsPerf test cases, I compared .filter to the super-slow while loop using .indexOf and .splice and the super-fast for loop:
As you can see, .filter() is a good choice ? a for loop is a bit faster, but .filter() is fine for removing all matching values from an array.
Which is fastest in a big array?
Of course, the above data examines tiny arrays ? and my browser is crushing 4 million while loops per second. Plenty of speed.
What if we are working with a bigger array, of say 10,000 items? In that case, performance depends a lot on the data you?re working with.
In a 10,000 item array where the target (the number 5,000) is only found one time, the while loop with .indexOf and .splice actually wins out:
For this use case, .filter is a big loser, as a for loop is about 5x faster. But .indexOf and .splice are more than twice as fast as the for loop.
Compare that to a 10,000 item array where the target (the number 5,000) is found as every other item in the array. The results are the exact same:
Thinking about what those numbers mean, .filter is only taking 0.25 milliseconds to process the array of 10,000 items ? still quite fast.
The take-home message is don?t engage in premature optimization.
Use the most readable code you can, then optimize only if necessary.
How to avoid mutation?
Note that Array.prototype.splice() modifies the array in place, which is generally good for performance, but you can get side effects (bugs).
Be aware that modifying an object, also called mutating it, is sometimes considered bad code practice, because of the possibility of side effects.
There is even an ESLint plugin (eslint-plugin-immutable) that disables all object mutation completely ? a good idea for preventing bugs.
But how would you remove an item from an array without mutating the original array? You just need to make a shallow copy of the array:
The for loop method already avoids mutation, because you are .pushing items to a new array. This is inherently a shallow copy of the array.
The usual methods of copying an object or array only make a shallow copy, so deeply-nested references are a problem?
To avoid mutating the array, make a shallow copy or use a for loop.
A for loop is also a great choice if you need to remove every matching value from an array? or just use .filter to filter out the matching items.
While combining .indexOf and .splice is slower than using a for loop for small arrays, the performance varies for bigger arrays.
My recommendation is to use the most readable versions in your code:
- .indexOf and .splice to remove just the first instance of a value
- .filter to remove every instance of a value from an array
Those methods are going to be inherently more self-documenting than a for loop, where you will need to write a comment explaining your code.
For the best performance when removing items from large arrays, consider .indexOf and .splice, as that method can be quite fast.
Happy coding! ???
Photo by Anne Nygrd on Unsplash