Master nested loops in JavaScript. Learn how to work with multi-dimensional data, create patterns, implement algorithms with nested iterations, and understand time complexity with practical examples.
Nested loops are loops placed inside other loops. The inner loop executes completely for each iteration of the outer loop. They are essential for working with multi-dimensional data, creating patterns, and implementing many algorithms.
How Nested Loops Work
┌─────────────────────────────────────────────────────────┐
│ NESTED LOOP EXECUTION FLOW │
└─────────────────────────────────────────────────────────┘
OUTER LOOP (i = 0 to 2)
┌─────────────────────────────────────────────┐
│ i = 0: │
│ INNER LOOP (j = 0 to 2) │
│ ┌──────────────────────────────────┐ │
│ │ j=0 → execute │ j=1 → execute │ │ │
│ │ j=2 → execute │ j=3 → EXIT │ │ │
│ └──────────────────────────────────┘ │
│ │
│ i = 1: │
│ INNER LOOP (j = 0 to 2) — FULL RESET │
│ ┌──────────────────────────────────┐ │
│ │ j=0 → execute │ j=1 → execute │ │ │
│ │ j=2 → execute │ j=3 → EXIT │ │ │
│ └──────────────────────────────────┘ │
│ │
│ i = 2: │
│ INNER LOOP (j = 0 to 2) — FULL RESET │
│ ┌──────────────────────────────────┐ │
│ │ j=0 → execute │ j=1 → execute │ │ │
│ │ j=2 → execute │ j=3 → EXIT │ │ │
│ └──────────────────────────────────┘ │
│ │
│ i = 3: EXIT OUTER LOOP │
└─────────────────────────────────────────────┘
Total iterations of inner body: 3 × 3 = 9
Basic Syntax
for (let i = 0; i < outerLimit; i++) {
// Outer loop body (runs outerLimit times)
for (let j = 0; j < innerLimit; j++) {
// Inner loop body (runs outerLimit × innerLimit times)
}
}
Execution Demonstration
for (let i = 1; i <= 3; i++) {
for (let j = 1; j <= 3; j++) {
console.log(`i=${i}, j=${j}`);
}
console.log("--- End of inner loop ---");
}
i=1, j=1
i=1, j=2
i=1, j=3
--- End of inner loop ---
i=2, j=1
i=2, j=2
i=2, j=3
--- End of inner loop ---
i=3, j=1
i=3, j=2
i=3, j=3
--- End of inner loop ---
Pattern Programs
Pattern 1: Right Triangle (Stars)
let rows = 5;
let output = "";
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= i; j++) {
output += "* ";
}
output += "\n";
}
console.log(output);
*
* *
* * *
* * * *
* * * * *
Pattern 2: Inverted Triangle
let rows = 5;
let output = "";
for (let i = rows; i >= 1; i--) {
for (let j = 1; j <= i; j++) {
output += "* ";
}
output += "\n";
}
console.log(output);
* * * * *
* * * *
* * *
* *
*
Pattern 3: Number Pyramid
let rows = 5;
let output = "";
for (let i = 1; i <= rows; i++) {
// Spaces for alignment
for (let s = 1; s <= rows - i; s++) {
output += " ";
}
// Numbers
for (let j = 1; j <= i; j++) {
output += j + " ";
}
output += "\n";
}
console.log(output);
1
1 2
1 2 3
1 2 3 4
1 2 3 4 5 Pattern 4: Diamond
let n = 5;
let output = "";
// Upper half
for (let i = 1; i <= n; i++) {
for (let s = 1; s <= n - i; s++) output += " ";
for (let j = 1; j <= 2 * i - 1; j++) output += "*";
output += "\n";
}
// Lower half
for (let i = n - 1; i >= 1; i--) {
for (let s = 1; s <= n - i; s++) output += " ";
for (let j = 1; j <= 2 * i - 1; j++) output += "*";
output += "\n";
}
console.log(output);
*
***
*****
*******
*********
*******
*****
***
*Pattern 5: Floyd's Triangle
let rows = 5;
let num = 1;
let output = "";
for (let i = 1; i <= rows; i++) {
for (let j = 1; j <= i; j++) {
output += num + "\t";
num++;
}
output += "\n";
}
console.log(output);
1
2 3
4 5 6
7 8 9 10
11 12 13 14 15
Pattern 6: Pascal's Triangle
let rows = 6;
let output = "";
for (let i = 0; i < rows; i++) {
let spaces = " ".repeat(rows - i - 1);
output += spaces;
let value = 1;
for (let j = 0; j <= i; j++) {
output += value + " ";
value = value * (i - j) / (j + 1);
}
output += "\n";
}
console.log(output);
1
1 1
1 2 1
1 3 3 1
1 4 6 4 1
1 5 10 10 5 1 Working with 2D Arrays (Matrices)
Traversing a Matrix
let matrix = [
[1, 2, 3],
[4, 5, 6],
[7, 8, 9]
];
console.log("Matrix:");
for (let i = 0; i < matrix.length; i++) {
let row = "";
for (let j = 0; j < matrix[i].length; j++) {
row += matrix[i][j] + "\t";
}
console.log(row);
}
Matrix:
1 2 3
4 5 6
7 8 9
Matrix Transpose
let original = [
[1, 2, 3],
[4, 5, 6]
];
let rows = original.length;
let cols = original[0].length;
let transposed = [];
for (let j = 0; j < cols; j++) {
transposed[j] = [];
for (let i = 0; i < rows; i++) {
transposed[j][i] = original[i][j];
}
}
console.log("Original (2×3):");
for (let row of original) console.log(" " + row.join(", "));
console.log("\nTransposed (3×2):");
for (let row of transposed) console.log(" " + row.join(", "));
Original (2×3):
1, 2, 3
4, 5, 6
Transposed (3×2):
1, 4
2, 5
3, 6
Matrix Multiplication
let A = [[1, 2], [3, 4]];
let B = [[5, 6], [7, 8]];
let result = [[0, 0], [0, 0]];
for (let i = 0; i < 2; i++) {
for (let j = 0; j < 2; j++) {
for (let k = 0; k < 2; k++) {
result[i][j] += A[i][k] * B[k][j];
}
}
}
console.log("A × B =");
for (let row of result) {
console.log(" [" + row.join(", ") + "]");
}
A × B =
[19, 22]
[43, 50]
Sorting Algorithms
Bubble Sort
let arr = [64, 34, 25, 12, 22, 11, 90];
console.log("Original: [" + arr.join(", ") + "]");
let n = arr.length;
let swaps = 0;
for (let i = 0; i < n - 1; i++) {
for (let j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
// Swap
let temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swaps++;
}
}
}
console.log("Sorted: [" + arr.join(", ") + "]");
console.log("Total swaps: " + swaps);
Original: [64, 34, 25, 12, 22, 11, 90]
Sorted: [11, 12, 22, 25, 34, 64, 90]
Total swaps: 14
Selection Sort
let arr = [29, 10, 14, 37, 13];
console.log("Original: [" + arr.join(", ") + "]");
for (let i = 0; i < arr.length - 1; i++) {
let minIdx = i;
for (let j = i + 1; j < arr.length; j++) {
if (arr[j] < arr[minIdx]) {
minIdx = j;
}
}
if (minIdx !== i) {
[arr[i], arr[minIdx]] = [arr[minIdx], arr[i]];
console.log(`Step ${i + 1}: Swapped index ${i} and ${minIdx} → [${arr.join(", ")}]`);
}
}
console.log("Final: [" + arr.join(", ") + "]");
Original: [29, 10, 14, 37, 13]
Step 1: Swapped index 0 and 1 → [10, 29, 14, 37, 13]
Step 2: Swapped index 1 and 4 → [10, 13, 14, 37, 29]
Step 3: Swapped index 3 and 4 → [10, 13, 14, 29, 37]
Final: [10, 13, 14, 29, 37]
Practical Programs
Program 1: Multiplication Table Grid
let size = 5;
let output = "×\t";
// Header row
for (let i = 1; i <= size; i++) output += i + "\t";
output += "\n" + "─".repeat(40) + "\n";
// Body
for (let i = 1; i <= size; i++) {
output += i + "│\t";
for (let j = 1; j <= size; j++) {
output += (i * j) + "\t";
}
output += "\n";
}
console.log(output);
× 1 2 3 4 5
────────────────────────────────────────
1│ 1 2 3 4 5
2│ 2 4 6 8 10
3│ 3 6 9 12 15
4│ 4 8 12 16 20
5│ 5 10 15 20 25
Program 2: Find Common Elements in Two Arrays
let arr1 = [1, 4, 7, 2, 9, 5];
let arr2 = [3, 2, 8, 4, 1, 6];
let common = [];
for (let i = 0; i < arr1.length; i++) {
for (let j = 0; j < arr2.length; j++) {
if (arr1[i] === arr2[j]) {
common.push(arr1[i]);
break; // No need to check further in arr2
}
}
}
console.log("Array 1: [" + arr1.join(", ") + "]");
console.log("Array 2: [" + arr2.join(", ") + "]");
console.log("Common: [" + common.join(", ") + "]");
Array 1: [1, 4, 7, 2, 9, 5]
Array 2: [3, 2, 8, 4, 1, 6]
Common: [1, 4, 2]
Program 3: Flatten a 2D Array
let nested = [[1, 2, 3], [4, 5], [6, 7, 8, 9], [10]];
let flat = [];
for (let i = 0; i < nested.length; i++) {
for (let j = 0; j < nested[i].length; j++) {
flat.push(nested[i][j]);
}
}
console.log("Nested: " + JSON.stringify(nested));
console.log("Flat: [" + flat.join(", ") + "]");
Nested: [[1,2,3],[4,5],[6,7,8,9],[10]]
Flat: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]
Program 4: Check if Array Has Duplicate Values
let values = [5, 3, 8, 1, 3, 9, 2];
let hasDuplicates = false;
let duplicateValue;
outer:
for (let i = 0; i < values.length; i++) {
for (let j = i + 1; j < values.length; j++) {
if (values[i] === values[j]) {
hasDuplicates = true;
duplicateValue = values[i];
break outer;
}
}
}
console.log("Array: [" + values.join(", ") + "]");
if (hasDuplicates) {
console.log(`Duplicate found: ${duplicateValue}`);
} else {
console.log("No duplicates found.");
}
Array: [5, 3, 8, 1, 3, 9, 2]
Duplicate found: 3
Program 5: Generate All Pairs
let items = ["A", "B", "C", "D"];
let pairs = [];
for (let i = 0; i < items.length; i++) {
for (let j = i + 1; j < items.length; j++) {
pairs.push(`(${items[i]}, ${items[j]})`);
}
}
console.log(`All unique pairs from [${items.join(", ")}]:`);
console.log(pairs.join(", "));
console.log(`Total pairs: ${pairs.length}`);
All unique pairs from [A, B, C, D]:
(A, B), (A, C), (A, D), (B, C), (B, D), (C, D)
Total pairs: 6
Time Complexity
┌────────────────────────────────────────────────────────┐
│ TIME COMPLEXITY OF NESTED LOOPS │
├─────────────────────────────┬──────────────────────────┤
│ Loop Structure │ Complexity │
├─────────────────────────────┼──────────────────────────┤
│ Single loop (n iterations) │ O(n) │
│ Two nested loops (n × n) │ O(n²) │
│ Three nested loops (n³) │ O(n³) │
│ Inner depends on outer │ O(n²/2) ≈ O(n²) │
│ Inner loop halves each time │ O(n log n) │
└─────────────────────────────┴──────────────────────────┘
// Demonstrating iteration counts
let n = 5;
let count1 = 0, count2 = 0, count3 = 0;
// O(n²) - Full nested
for (let i = 0; i < n; i++)
for (let j = 0; j < n; j++)
count1++;
// O(n²/2) - Triangular
for (let i = 0; i < n; i++)
for (let j = i; j < n; j++)
count2++;
// O(n log n) - Halving inner
for (let i = 0; i < n; i++)
for (let j = n; j > 0; j = Math.floor(j / 2))
count3++;
console.log(`n = ${n}`);
console.log(`Full nested (O(n²)): ${count1} iterations`);
console.log(`Triangular (O(n²/2)): ${count2} iterations`);
console.log(`Halving inner (O(nlogn)): ${count3} iterations`);
n = 5
Full nested (O(n²)): 25 iterations
Triangular (O(n²/2)): 15 iterations
Halving inner (O(nlogn)): 15 iterations
Common Mistakes
Mistake 1: Using Same Variable Name
// ❌ Using 'i' for both loops (var has function scope!)
// for (var i = 0; i < 3; i++) {
// for (var i = 0; i < 3; i++) { // Overwrites outer i!
// console.log(i);
// }
// }
// ✅ Use different names or let (block-scoped)
for (let i = 0; i < 3; i++) {
for (let j = 0; j < 3; j++) {
// Each has its own scope
}
}
console.log("Use different variable names: i, j, k");
Use different variable names: i, j, k
Mistake 2: Unnecessary Nested Loops
// ❌ O(n²) approach to find element
let arr = [5, 3, 8, 1, 9, 2, 7];
let target = 8;
let found = false;
// Don't need nested loop for linear search!
for (let i = 0; i < arr.length; i++) {
if (arr[i] === target) {
found = true;
break;
}
}
// ✅ Even better: use built-in methods
let exists = arr.includes(target);
console.log(`Found ${target}: ${exists}`);
Optimization Tips
// 1. Cache array length
let matrix = [[1,2,3],[4,5,6],[7,8,9]];
let rows = matrix.length;
for (let i = 0; i < rows; i++) {
let cols = matrix[i].length; // Cache inner length
for (let j = 0; j < cols; j++) {
// Process matrix[i][j]
}
}
// 2. Break early when possible
// 3. Move invariant calculations outside inner loop
// 4. Consider Set/Map for O(1) lookups instead of nested search
// Example: Finding common elements with Set (O(n) instead of O(n²))
let arr1 = [1, 4, 7, 2, 9];
let arr2 = [3, 2, 8, 4, 1];
let set1 = new Set(arr1);
let common = arr2.filter(item => set1.has(item));
console.log("Common (O(n) approach): " + common.join(", "));
Common (O(n) approach): 2, 4, 1
Best Practices
- Minimize nesting depth — extract inner loops into functions.
- Use appropriate data structures — Sets/Maps can eliminate nested loops.
- Break early — use labeled breaks when the answer is found.
- Use descriptive variable names —
row/col instead of i/j for matrices. - Be aware of time complexity — O(n²) can be slow for large datasets.
- Consider array methods —
map(), flat(), flatMap() often replace nested loops.
Interview Questions
Q1: What is the time complexity of two nested for loops each running n times?
Answer: O(n²) — quadratic time. The inner loop runs n times for each of the n iterations of the outer loop, giving n × n = n² total operations.
Q2: How can you break out of both inner and outer loops?
Answer: Use a labeled statement with break label, extract into a function and use return, or use a flag variable checked by the outer loop.
Q3: What's wrong with using var for nested loop counters?
Answer: var is function-scoped, so using the same variable name in both loops causes the inner loop to overwrite the outer counter. let is block-scoped and creates separate variables per loop.
Q4: How do you optimize nested loops for finding common elements?
Answer: Instead of O(n²) nested loops, use a Set for O(n): add one array's elements to a Set, then filter the second array by checking Set membership (O(1) per lookup).
Q5: When are nested loops unavoidable?
Answer: Matrix operations (transpose, multiply), generating all pairs/combinations, pattern printing, certain sorting algorithms (bubble sort, insertion sort), and graph algorithms (adjacency matrix traversal).
Q6: What is the difference between O(n²) and O(n²/2) in nested loops?
Answer: Mathematically O(n²/2) simplifies to O(n²) in Big-O notation because constants are dropped. However, the actual running time is halved (e.g., triangular iteration j = i to n vs full j = 0 to n).
Q7: How do you iterate over a 2D array column-first instead of row-first?
Answer: Swap the loop order: outer loop iterates columns (j), inner loop iterates rows (i). Access as matrix[i][j] but with j as outer.
Q8: How can you replace nested loops with array methods?
Answer: flatMap() for flattening with transformation, flat() for flattening, reduce() for accumulation, .some()/.every() combined for multi-level checks.
Q9: What happens when you nest different types of loops?
Answer: Any combination works (for inside while, while inside for, etc.). The execution follows the same principle: inner loop completes fully for each outer iteration.
Q10: How do you count total iterations in nested loops with different bounds?
Answer: Multiply the iteration counts of each loop. If outer runs m times and inner runs n times, total = m × n. If inner depends on outer (e.g., j = 0 to i), sum the series: Σ(i=0 to n-1) of i = n(n-1)/2.
Q11: What is the "early termination" optimization for nested loops?
Answer: Breaking out of nested loops as soon as the answer is found. Without it, unnecessary iterations waste time. For search operations, this can turn worst-case O(n²) into best-case O(1).
Q12: How do nested loops relate to recursion?
Answer: Many nested loop patterns can be expressed as recursion and vice versa. Recursion can handle variable nesting depth (e.g., generating all subsets of any size), while nested loops are fixed at compile time. Recursion trades stack space for flexibility.