indexOf
loops
coding best practices
performance optimization
JavaScript

Is it a bad idea to use indexOf inside loops?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

Using indexOf() inside a loop is not automatically wrong, but it is often a signal that you should check both performance and correctness. The main issue is that indexOf() scans from the start of the array each time, so repeating it inside another loop can turn a simple task into an unnecessarily expensive one.

Why indexOf() in a Loop Can Be Slow

Array.prototype.indexOf() performs a linear search. If an array has n items, one call can inspect up to n elements. If you place that search inside a loop that also runs n times, the combined work can grow toward n^2.

Here is a common pattern:

javascript
1const allowed = ["red", "green", "blue"];
2const input = ["green", "yellow", "blue", "red"];
3
4for (const color of input) {
5  if (allowed.indexOf(color) !== -1) {
6    console.log(`${color} is allowed`);
7  }
8}

This works, and for three allowed values it is perfectly fine. The problem appears when allowed becomes large and the loop runs frequently. Then the repeated scans become measurable.

When It Is Completely Fine

Do not optimize blindly. If the array is tiny, the loop runs rarely, and the code is clear, indexOf() may be the right choice. Performance discussions only matter relative to the size of the data and how often the code executes.

For example, scanning a list of seven weekday names once during form validation is not a real bottleneck. Rewriting that code into something more complex would likely make maintenance worse, not better.

The better question is not "is this always bad?" but "does the repeated search add real cost in this path?"

Better Alternatives for Common Cases

If you are checking membership repeatedly, convert the lookup collection into a Set. A Set is designed for fast membership tests.

javascript
1const allowed = new Set(["red", "green", "blue"]);
2const input = ["green", "yellow", "blue", "red"];
3
4for (const color of input) {
5  if (allowed.has(color)) {
6    console.log(`${color} is allowed`);
7  }
8}

This is usually the best replacement when the logic is "does this item exist in a collection?"

If you are using indexOf() only to find the current position of the element you are already iterating, use the loop index instead.

javascript
1const values = ["a", "b", "c"];
2
3for (let i = 0; i < values.length; i += 1) {
4  console.log(i, values[i]);
5}

Or use entries() when you want both index and value in a for...of loop:

javascript
1const values = ["a", "b", "c"];
2
3for (const [index, value] of values.entries()) {
4  console.log(index, value);
5}

That avoids rescanning the array just to recover information you already have.

Correctness Problems With Duplicates

Performance is not the only issue. indexOf() returns the first matching position, not the current one. That can silently produce wrong results when the array contains duplicates.

javascript
1const values = ["x", "y", "x"];
2
3for (const value of values) {
4  console.log(value, values.indexOf(value));
5}

Output:

text
x 0
y 1
x 0

The last "x" is actually at index 2, but indexOf() still returns 0 because that is the first match. If your code uses the result to update state, render a label, or remove an item, that bug can be surprisingly hard to spot.

When duplicates are possible, use the actual loop index instead of recalculating it with indexOf().

Choosing the Right Tradeoff

There are three practical rules:

  1. If you need membership checks many times, prefer Set.
  2. If you need the current index during iteration, use a loop that gives you the index directly.
  3. If the array is small and the code path is not hot, indexOf() is acceptable.

Developers sometimes replace indexOf() with includes(). That can make the intent clearer when you only care about existence:

javascript
if (allowed.includes(color)) {
  console.log("allowed");
}

This improves readability, but it does not change the complexity. includes() still scans linearly.

Common Pitfalls

  • Using indexOf() inside a loop to get the current index, which breaks when duplicate values exist.
  • Repeating indexOf() on large arrays in frequently executed code paths.
  • Replacing indexOf() with includes() and assuming the performance problem is solved.
  • Optimizing away a simple indexOf() call when the dataset is tiny and the code runs rarely.
  • Building a Set inside the loop instead of once before the loop, which removes the benefit.

Summary

  • 'indexOf() inside loops is not always wrong, but it often causes unnecessary repeated linear scans.'
  • The main risk is both performance on large inputs and incorrect results with duplicate values.
  • Use Set.has() for repeated membership tests.
  • Use the loop index directly when you need the current position.
  • Keep the simple version when the data is small and the code path is not performance-sensitive.

Course illustration
Course illustration

All Rights Reserved.