Question

Why for...in Is Bad for Array Iteration in JavaScript

javascriptarraysloopsfor-loopiteration

Question

Why is using for...in to iterate over arrays considered a bad practice in JavaScript? What problems can it cause, and which alternatives should be used when looping through array values?

Short Answer

By the end of this page, you will understand that for...in iterates over property names rather than reliably iterating over array elements. You will know when to use for...of, classic for loops, and array methods such as forEach, map, and filter instead.

Concept

for...in is designed to enumerate an object's enumerable property keys. An array is also an object in JavaScript, so for...in can list more than its numeric indexes.

For example, an array normally has keys such as "0", "1", and "2". However, it can also have custom properties, and it may inherit enumerable properties from Array.prototype or another prototype. A for...in loop may include those properties too.

const colors = ["red", "green", "blue"];

for (const key in colors) {
  console.log(key);
}

// "0"
// "1"
// "2"

Notice that the loop variable contains keys as strings, not the values "red", "green", and "blue".

This matters because array iteration usually means processing values in order. for...in does not communicate that intent well, can include unexpected properties, and should not be relied on for array ordering. Use an array-focused construct instead.

Mental Model

Think of an array as a numbered shelf of books:

  • for...of says: “Give me each book on the shelf.”
  • A classic for loop says: “Visit shelf position 0, then 1, then 2.”
  • for...in says: “Tell me every label attached to this shelf object.”

The labels include the expected position labels ("0", "1") but might also include extra labels someone attached later. That is useful when inspecting an object's properties, but it is not a safe way to read the books themselves.

Take Quiz

Syntax and Examples

Use for...of when you need array values.

const fruits = ["apple", "banana", "orange"];

for (const fruit of fruits) {
  console.log(fruit);
}

// apple
// banana
// orange

Use entries() when you need both the index and value.

const fruits = ["apple", "banana", "orange"];

for (const [index, fruit] of fruits.entries()) {
  console.log(`${index}: ${fruit}`);
}

// 0: apple
// 1: banana
// 2: orange

A for...in loop produces property names instead:

const fruits = ["apple", "banana", "orange"];

for (const key in fruits) {
  console.log(key, fruits[key]);
}

// 0 apple
// 1 banana
// 2 orange

Although fruits[key] works for ordinary arrays, for...in can also visit non-index properties. That is why it is not the preferred array loop.

Step by Step Execution

Consider this code:

const tasks = ["email client", "write report"];
tasks.priority = "high";

for (const key in tasks) {
  console.log(key, tasks[key]);
}

Step by step:

  1. tasks is an array with elements at indexes 0 and 1.
  2. tasks.priority = "high" adds a normal object property to the array.
  3. for...in looks for enumerable property keys on tasks.
  4. It can visit the keys "0", "1", and "priority".
  5. The output can therefore be:
0 email client
1 write report
priority high

The priority property is not an array element, but for...in treats it as another enumerable property. By contrast, for...of only iterates the array's iterable values:

for (const task of tasks) {
  console.log(task);
}

// email client
// write report

Real World Use Cases

Choose the loop based on the work you need to do:

  • Render a list in a command-line script: use for...of to process each filename, user, or product.
  • Validate API data: use for...of to inspect every item in an array returned by an API.
  • Create transformed data: use map() to turn product prices into formatted display strings.
  • Remove unwanted records: use filter() to keep only active users.
  • Stop after finding a match: use for...of with break, or find() when you only need the matching value.
  • Inspect object configuration: use for...in only when you intentionally want an object's enumerable property keys, usually with an own-property check.
const prices = [12, 25, 8];
const formattedPrices = prices.map(price => `$${price.toFixed(2)}`);

console.log(formattedPrices);
// ["$12.00", "$25.00", "$8.00"]
Take Quiz

Real Codebase Usage

In production code, developers make the operation's intent clear.

Process every value

for (const user of users) {
  sendWelcomeEmail(user);
}

Transform without changing the original array

const userNames = users.map(user => user.name);

Filter invalid values before continuing

const validOrders = orders.filter(order => order.total > 0);

Use a guard clause in a loop

for (const order of orders) {
  if (!order.isPaid) {
    continue;
  }

  shipOrder(order);
}

Iterate object keys intentionally

For plain objects, Object.keys() makes it clear that you want own keys only:

const settings = { theme: "dark", pageSize: 20 };

for (const key of Object.keys(settings)) {
  console.log(key, settings[key]);
}

Avoid adding custom enumerable properties to arrays. Store related metadata in a separate object or use an object with clearly named fields instead.

Common Mistakes

Treating for...in values as array elements

for...in returns keys, so this code logs indexes rather than names:

const names = ["Ada", "Lin"];

for (const name in names) {
  console.log(name);
}

// 0
// 1

Use for...of:

for (const name of names) {
  console.log(name);
}

Assuming only indexes will be visited

const items = ["pen", "notebook"];
items.category = "stationery";

for (const key in items) {
  console.log(key);
}

The loop can include "category". Do not use for...in when processing array elements.

Assuming keys are numbers

Array property keys from for...in are strings.

const values = [10, 20];

for (const key in values) {
  console.log(typeof key); // "string"
}

Use entries() if you need an index alongside each value. The index provided by entries() is numeric.

Using forEach() when you need break

This does not stop the forEach loop:

[1, 2, 3].forEach(number => {
  if (number === 2) {
    return;
  }
  console.log(number);
});

return exits only that callback invocation. Use for...of when you need break or continue.

for (const number of [1, 2, 3]) {
  if (number === 2) {
    break;
  }
  console.log(number);
}

Comparisons

OptionIterates overBest useImportant note
for...inEnumerable property keysObject-property enumerationNot recommended for arrays; keys are strings and extras may appear.
for...ofIterable valuesReading array values in sequenceSupports break, continue, and await in an async function.
Classic forNumeric index you controlIndex-based logic or maximum controlUseful when comparing nearby elements or changing indexes.
array.forEach()Array valuesSimple side effects for every itemCannot use break or continue.
array.map()Array valuesCreating a new transformed arrayReturn a value from the callback.
Object.keys(object)Own enumerable object keysIterating a plain object's keysReturns an array of keys.

A classic index loop looks like this:

const scores = [90, 75, 88];

for (let index = 0; index < scores.length; index++) {
  console.log(index, scores[index]);
}
Take Quiz

Cheat Sheet

// Preferred: values
for (const value of array) {
  // use value
}

// Preferred: index and value
for (const [index, value] of array.entries()) {
  // use index and value
}

// Preferred: controlled index loop
for (let index = 0; index < array.length; index++) {
  // use array[index]
}

// Transform values into a new array
const result = array.map(value => transform(value));

// Keep matching values
const result = array.filter(value => matches(value));

// Iterate a plain object's own keys
for (const key of Object.keys(object)) {
  // use object[key]
}

Rules to remember:

  • Do not use for...in for normal array iteration.
  • for...in yields property names, not values.
  • for...in keys are strings.
  • for...in can include custom or inherited enumerable properties.
  • Use for...of for readable value-by-value array loops.
  • Use for...of rather than forEach() when you need break or continue.

FAQ

Why does for...in return strings for array indexes?

JavaScript object property names are strings (apart from symbols). Array indexes are represented as property keys, so for...in produces values such as "0" and "1".

Does for...in always fail with arrays?

No. It often appears to work for a simple array with no extra properties. The problem is that it has the wrong semantics and can produce unexpected keys as code changes.

Should I use for...of or forEach() for arrays?

Use for...of when you may need break, continue, or await. Use forEach() for a short callback that performs a side effect for every item. Use map() when creating a new array.

Can for...of give me the index too?

Yes. Use array.entries():

for (const [index, value] of array.entries()) {
  console.log(index, value);
}

Is for...in useful at all?

Yes. It can enumerate enumerable property keys on objects. For predictable own-property iteration, Object.keys(), Object.values(), or Object.entries() is often clearer.

Does for...of work on plain objects?

Usually no. Plain objects are not iterable by default. Use Object.keys(object), Object.values(object), or Object.entries(object).

Does for...of include custom properties added to an array?

No. for...of follows the array's iterator and yields its element values, not arbitrary custom properties such as array.status.

Take Quiz

Mini Project

Description

Build a small task-reporting script that loops through a list of tasks safely. The script demonstrates why task metadata should be stored separately rather than attached directly to an array, and why for...of is the correct tool for processing array values.

Goal

Display each task, count completed tasks, and show project metadata without accidentally treating metadata as a task.

Requirements

  • Create an array containing task objects with title and completed properties.
  • Store project metadata in a separate object.
  • Use for...of to display every task and count completed tasks.
  • Print a final summary containing the project name and completed-task count.
  • Do not use for...in to iterate over the tasks array.
Take Quiz

Keep learning

Related questions

View more