How to Write Nested Loops in JavaScript: Tutorial

Learn how to write nested loops in JavaScript with practical examples. Covers nested for loops, grid processing, matrix operations, pattern printing, performance considerations, and when to flatten nested structures.

JavaScriptbeginner
10 min read

A nested loop is a loop inside another loop. The inner loop runs completely for every single iteration of the outer loop. If the outer loop runs 5 times and the inner loop runs 10 times, the inner body executes 50 times total. Nested loops are essential for working with two-dimensional data: grids, matrices, tables, combinations, and any structure where you need to process rows and columns.

This tutorial covers nested for loops, practical examples with grids and matrices, using break and continue in nested contexts, performance implications, and guidelines for when to avoid nesting.

Basic Nested Loop Structure

javascriptjavascript
for (let outer = 0; outer < 3; outer++) {
  for (let inner = 0; inner < 4; inner++) {
    console.log(`outer: ${outer}, inner: ${inner}`);
  }
}

The inner loop runs 4 full iterations for each of the 3 outer iterations, producing 12 total log statements:

OuterInner iterationsOutputs
00, 1, 2, 3(0,0) (0,1) (0,2) (0,3)
10, 1, 2, 3(1,0) (1,1) (1,2) (1,3)
20, 1, 2, 3(2,0) (2,1) (2,2) (2,3)

Total body executions: 3 x 4 = 12

Name Your Counters

Avoid generic i and j in nested loops. Use meaningful names like row and col, student and grade, or dept and team. Clear names prevent bugs where you accidentally use the wrong counter variable.

Processing a 2D Array (Matrix)

The most common use case for nested loops is iterating over rows and columns of a 2D array:

javascriptjavascript
const matrix = [
  [1, 2, 3],
  [4, 5, 6],
  [7, 8, 9],
];
 
for (let row = 0; row < matrix.length; row++) {
  for (let col = 0; col < matrix[row].length; col++) {
    console.log(`[${row}][${col}] = ${matrix[row][col]}`);
  }
}

Summing All Elements

javascriptjavascript
const grid = [
  [10, 20, 30],
  [40, 50, 60],
  [70, 80, 90],
];
 
let total = 0;
 
for (let row = 0; row < grid.length; row++) {
  for (let col = 0; col < grid[row].length; col++) {
    total += grid[row][col];
  }
}
 
console.log(`Sum: ${total}`); // Sum: 450

Using for...of for Cleaner 2D Iteration

When you do not need the index:

javascriptjavascript
const data = [
  ["Alice", "Bob", "Carol"],
  ["Dave", "Eve", "Frank"],
];
 
for (const row of data) {
  for (const name of row) {
    console.log(name);
  }
}
// Alice, Bob, Carol, Dave, Eve, Frank

Multiplication Table

A classic nested loop example that builds a formatted table:

javascriptjavascript
const size = 10;
 
// Print header row
let header = "    ";
for (let col = 1; col <= size; col++) {
  header += String(col).padStart(5);
}
console.log(header);
console.log("-".repeat(header.length));
 
// Print table rows
for (let row = 1; row <= size; row++) {
  let line = String(row).padStart(3) + " ";
  for (let col = 1; col <= size; col++) {
    line += String(row * col).padStart(5);
  }
  console.log(line);
}

Output:

CodeCode
         1    2    3    4    5    6    7    8    9   10
----------------------------------------------------
  1     1    2    3    4    5    6    7    8    9   10
  2     2    4    6    8   10   12   14   16   18   20
  3     3    6    9   12   15   18   21   24   27   30
  ...

Finding Pairs and Combinations

All Unique Pairs

javascriptjavascript
const items = ["A", "B", "C", "D"];
 
for (let i = 0; i < items.length; i++) {
  for (let j = i + 1; j < items.length; j++) {
    console.log(`${items[i]} - ${items[j]}`);
  }
}
// A - B, A - C, A - D, B - C, B - D, C - D

Starting the inner counter at i + 1 avoids duplicate pairs. Without this, you would get both "A - B" and "B - A".

Finding Two Numbers That Sum to a Target

javascriptjavascript
function twoSum(numbers, target) {
  for (let i = 0; i < numbers.length; i++) {
    for (let j = i + 1; j < numbers.length; j++) {
      if (numbers[i] + numbers[j] === target) {
        return [i, j];
      }
    }
  }
  return null;
}
 
console.log(twoSum([2, 7, 11, 15], 9)); // [0, 1]
console.log(twoSum([3, 4, 5, 6], 11));  // [2, 3]

Cartesian Product

javascriptjavascript
const colors = ["red", "blue"];
const sizes = ["S", "M", "L"];
 
const combinations = [];
 
for (const color of colors) {
  for (const size of sizes) {
    combinations.push({ color, size });
  }
}
 
console.log(combinations);
// [{ color: "red", size: "S" }, { color: "red", size: "M" },
//  { color: "red", size: "L" }, { color: "blue", size: "S" },
//  { color: "blue", size: "M" }, { color: "blue", size: "L" }]

Pattern Printing

Right Triangle

javascriptjavascript
const rows = 5;
 
for (let i = 1; i <= rows; i++) {
  let line = "";
  for (let j = 0; j < i; j++) {
    line += "* ";
  }
  console.log(line);
}
// *
// * *
// * * *
// * * * *
// * * * * *

Inverted Triangle

javascriptjavascript
const rows = 5;
 
for (let i = rows; i >= 1; i--) {
  let line = "";
  for (let j = 0; j < i; j++) {
    line += "* ";
  }
  console.log(line);
}
// * * * * *
// * * * *
// * * *
// * *
// *

Number Pyramid

javascriptjavascript
const rows = 5;
 
for (let i = 1; i <= rows; i++) {
  let spaces = " ".repeat(rows - i);
  let numbers = "";
  for (let j = 1; j <= i; j++) {
    numbers += j + " ";
  }
  console.log(spaces + numbers);
}
//     1
//    1 2
//   1 2 3
//  1 2 3 4
// 1 2 3 4 5

Break and Continue in Nested Loops

Breaking the Inner Loop Only

javascriptjavascript
const classrooms = [
  { room: "101", students: ["Alice", "Bob", "Carol"] },
  { room: "102", students: ["Dave", "Eve", "Frank"] },
  { room: "103", students: ["Grace", "Hank", "Ivy"] },
];
 
for (const classroom of classrooms) {
  let found = false;
  for (const student of classroom.students) {
    if (student === "Eve") {
      console.log(`Found Eve in room ${classroom.room}`);
      found = true;
      break; // exits inner loop only
    }
  }
  if (found) break; // exits outer loop too
}

Labeled Break for Cleaner Nested Exit

javascriptjavascript
search:
for (const classroom of classrooms) {
  for (const student of classroom.students) {
    if (student === "Eve") {
      console.log(`Found Eve in room ${classroom.room}`);
      break search; // exits both loops
    }
  }
}

Continue in Nested Loops

javascriptjavascript
const data = [
  [1, -1, 3],
  [4, 5, -2],
  [-3, 8, 9],
];
 
const positives = [];
 
for (const row of data) {
  for (const value of row) {
    if (value < 0) continue; // skip negatives, inner loop continues
    positives.push(value);
  }
}
 
console.log(positives); // [1, 3, 4, 5, 8, 9]

Three-Level Nesting

Sometimes you need three levels. Keep the nesting readable with meaningful variable names:

javascriptjavascript
const school = {
  departments: [
    {
      name: "Science",
      classes: [
        { name: "Physics", students: ["Alice", "Bob"] },
        { name: "Chemistry", students: ["Carol", "Dave"] },
      ],
    },
    {
      name: "Arts",
      classes: [
        { name: "Music", students: ["Eve"] },
        { name: "Drama", students: ["Frank", "Grace"] },
      ],
    },
  ],
};
 
for (const dept of school.departments) {
  for (const cls of dept.classes) {
    for (const student of cls.students) {
      console.log(`${dept.name} > ${cls.name} > ${student}`);
    }
  }
}
Limit Nesting Depth

Three levels of nesting is the practical maximum for readability. Beyond three, refactor the inner logic into a separate function. A function call inside a loop is much easier to understand than four or five levels of indentation.

Performance Implications

Nested loops multiply iteration counts. This matters for large datasets:

OuterInnerTotal iterationsTime at 1M ops/sec
1010100Instant
10010010,000Instant
1,0001,0001,000,000~1 second
10,00010,000100,000,000~100 seconds
javascriptjavascript
// O(n^2): quadratic time
for (let i = 0; i < arr.length; i++) {
  for (let j = 0; j < arr.length; j++) {
    // runs arr.length * arr.length times
  }
}
 
// Often reducible to O(n) with a Set or Map
const seen = new Set();
for (const item of arr) {
  if (seen.has(item)) {
    console.log("Duplicate found:", item);
  }
  seen.add(item);
}

When to Avoid Nested Loops

javascriptjavascript
// SLOW: O(n^2) duplicate check
function hasDuplicates(arr) {
  for (let i = 0; i < arr.length; i++) {
    for (let j = i + 1; j < arr.length; j++) {
      if (arr[i] === arr[j]) return true;
    }
  }
  return false;
}
 
// FAST: O(n) with Set
function hasDuplicates(arr) {
  return new Set(arr).size !== arr.length;
}

Flattening Nested Arrays

javascriptjavascript
// Manual flatten with nested loops
const nested = [[1, 2], [3, 4, 5], [6]];
const flat = [];
 
for (const subArray of nested) {
  for (const item of subArray) {
    flat.push(item);
  }
}
 
console.log(flat); // [1, 2, 3, 4, 5, 6]
 
// Built-in alternative
console.log(nested.flat()); // [1, 2, 3, 4, 5, 6]

Matrix Operations

Transpose a Matrix

javascriptjavascript
function transpose(matrix) {
  const rows = matrix.length;
  const cols = matrix[0].length;
  const result = [];
 
  for (let col = 0; col < cols; col++) {
    const newRow = [];
    for (let row = 0; row < rows; row++) {
      newRow.push(matrix[row][col]);
    }
    result.push(newRow);
  }
 
  return result;
}
 
const original = [
  [1, 2, 3],
  [4, 5, 6],
];
 
console.log(transpose(original));
// [[1, 4], [2, 5], [3, 6]]

Best Practices

Use meaningful counter names. Replace i, j, k with row, col, layer or dept, team, member. Meaningful names make it immediately clear which counter belongs to which loop.

Limit nesting to three levels. Beyond three, extract inner loops into named functions. A function call is easier to understand than deep indentation.

Consider data structure alternatives. When nested loops exist to search (like finding duplicates or matching pairs), a Set, Map, or pre-sorted array often reduces complexity from O(n^2) to O(n) or O(n log n).

Break early when possible. If you are searching for a single result, use break or return as soon as you find it. Processing the remaining iterations wastes time on large datasets.

Watch the multiplication. Before writing nested loops over large collections, estimate the total iteration count. If it exceeds a million, profile the code and consider optimization.

Rune AI

Rune AI

Key Insights

  • Inner loop runs completely for each outer iteration: total iterations = outer count times inner count
  • Use meaningful variable names: row/col instead of i/j prevents counter confusion
  • Limit to three nesting levels: beyond three, extract inner logic into named functions
  • Watch the math: nested loops multiply iteration counts; estimate before running on large data
  • Consider alternatives: Sets, Maps, and .flat() often replace nested loops with better performance
RunePowered by Rune AI

Frequently Asked Questions

How many levels of nested loops can I have?

JavaScript has no syntactic limit on nesting depth. You can nest loops 10 levels deep if you want. However, readability drops sharply after two or three levels. Beyond three, refactor inner loops into separate functions. Performance is the other concern: each additional nesting level multiplies the iteration count.

Are nested loops always O(n^2)?

No. Two nested loops are O(n*m) where n and m are the iteration counts of each loop. If both loops iterate over the same collection of size n, it is O(n^2). But if the outer loop runs n times and the inner loop runs a fixed number (like 3 columns), the complexity is O(3n) = O(n), which is linear.

How do I break out of all nested loops at once?

Use a labeled break: place a label (like `outerLoop:`) before the outer loop, then use `break outerLoop;` inside the inner loop. Alternatively, extract the nested loops into a function and use `return` to exit.

Can I mix different loop types in nested loops?

Yes. You can nest a [while loop](/tutorials/programming-languages/javascript/javascript-while-loop-explained-a-complete-guide) inside a for loop, a for...of inside a while, or any combination. Each loop type can be used at any nesting level. Choose the loop type that best fits each level's iteration pattern.

When should I use flat() instead of nested loops?

Use `.flat()` when you want to flatten nested arrays into a single array without processing each element individually. If you need to transform, filter, or perform side effects during iteration, nested loops give you more control. For simple flattening, `.flat()` is a one-line solution.

Conclusion

Nested loops let you process multi-dimensional data by running an inner loop completely for each iteration of an outer loop. They are essential for grids, matrices, combinations, and pattern generation. The key discipline is managing the iteration count: nested loops multiply, so a 1,000-item outer loop with a 1,000-item inner loop runs one million iterations. Use meaningful counter names, limit nesting to three levels, break early when searching, and consider data structure alternatives like Sets and Maps when nested loops exist only to compare elements.