How to Find the Indexes of All Occurrences of an Element in a JavaScript Array
While JavaScript's indexOf() method is great for finding the first index of an element, a common and more advanced task is to find the indexes of all occurrences of that element. This is useful for data analysis, highlighting all instances of a search term, or replacing multiple items in a list.
This guide will teach you the most effective and readable methods for this task. We will cover the functional reduce() approach and the classic forEach loop, explaining the use case for each and why they are the standard solutions for this problem.
The Core Problem: Finding More Than Just the First Index
The indexOf() method stops as soon as it finds the first match, which is not what we need.
For example:
// Problem: How to find all indexes of 'a', which are 0, 2, and 4?
const letters = ['a', 'b', 'a', 'c', 'a'];
// indexOf() only gives us the first one.
console.log(letters.indexOf('a')); // Output: 0
To solve this, we must iterate through the entire array and collect the index of every matching element.
The Functional Method (Recommended): Array.prototype.reduce()
The reduce() method is a powerful and concise tool for this job. It is designed to "reduce" an array to a single value, which in our case will be the array of indexes we are building.
The logic:
- Use
.reduce()to iterate over the array. Thereducecallback provides access to theaccumulator(the array of indexes we are building), thecurrentValue, and thecurrentIndex. - Initialize the accumulator as an empty array (
[]). - On each iteration, check if the
currentValuematches our target. - If it does,
pushthecurrentIndexinto theaccumulator. - Always return the
accumulatorfor the next iteration.
This is the recommended functional approach.
const letters = ['a', 'b', 'a', 'c', 'a'];
const target = 'a';
const indexes = letters.reduce((accumulator, currentValue, currentIndex) => {
if (currentValue === target) {
accumulator.push(currentIndex);
}
return accumulator;
}, []);
console.log(indexes); // Output: [0, 2, 4]
The Manual Looping Method: forEach() or for...of
The classic imperative approach is to create an empty array and manually populate it within a loop. This is more verbose but can be easier to understand for beginners.
The forEach() method provides both the element and its index in its callback, making it a good choice for this.
const letters = ['a', 'b', 'a', 'c', 'a'];
const target = 'a';
const indexes = [];
letters.forEach((element, index) => {
if (element === target) {
indexes.push(index);
}
});
console.log(indexes); // Output: [0, 2, 4]
While this works perfectly, the reduce() method is often preferred in modern JavaScript for its conciseness and declarative, functional style.
A Clever (but Less Readable) Alternative: map() and filter()
You can also achieve this result by chaining map() and filter(). This method is clever but generally considered less readable and less direct than the reduce() or forEach() approaches.
The logic:
- Use
map()to transform the array into a new array that contains either the index (if the element matches) or a placeholder value (likenullor-1). - Use
filter()to remove all the placeholder values, leaving only the valid indexes.
Solution:
const letters = ['a', 'b', 'a', 'c', 'a'];
const target = 'a';
const indexes = letters
.map((element, index) => (element === target ? index : null))
.filter(index => index !== null);
console.log(indexes); // Output: [0, 2, 4]
Conclusion
For finding all indexes of an element in an array, modern JavaScript offers several effective solutions.
- The
reduce()method is the recommended functional best practice. It is concise, declarative, and encapsulates the logic in a single, fluent operation. - The
forEach()loop is a perfectly valid and highly readable imperative alternative. - The
map().filter()chain is a clever but less direct approach that is generally not recommended over the other two methods for this specific task.