JavaScript
Array Manipulation
Debugging
Programming Tips
Code Troubleshooting

Creating an array consisting of the largest values of each sub-array does not work as expected

Master System Design with Codemia

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

Introduction

A common JavaScript task is extracting the maximum value from each sub-array in a 2D array. The typical bug comes from using Math.max() incorrectly, such as passing an entire array instead of individual numbers, or initializing the comparison variable wrong. The correct approaches use Math.max(...subArray) with the spread operator, reduce(), or map() with proper handling of edge cases like empty sub-arrays.

The Bug

javascript
1function largestOfEach(arr) {
2    let results = [];
3    for (let i = 0; i < arr.length; i++) {
4        let largest = 0;  // BUG: assumes all values are positive
5        for (let j = 0; j < arr[i].length; j++) {
6            if (arr[i][j] > largest) {
7                largest = arr[i][j];
8            }
9        }
10        results.push(largest);
11    }
12    return results;
13}
14
15console.log(largestOfEach([[1, 2, 3], [-5, -3, -1], [10, 20]]));
16// Expected: [3, -1, 20]
17// Actual:   [3, 0, 20]  (0 is returned for the negative sub-array)

Initializing largest to 0 means any sub-array with only negative numbers returns 0 instead of the actual largest value.

Fix 1: Initialize with -Infinity

javascript
1function largestOfEach(arr) {
2    let results = [];
3    for (let i = 0; i < arr.length; i++) {
4        let largest = -Infinity;  // Correct: any number is greater than -Infinity
5        for (let j = 0; j < arr[i].length; j++) {
6            if (arr[i][j] > largest) {
7                largest = arr[i][j];
8            }
9        }
10        results.push(largest);
11    }
12    return results;
13}
14
15console.log(largestOfEach([[1, 2, 3], [-5, -3, -1], [10, 20]]));
16// [3, -1, 20] ✓

Fix 2: Initialize with First Element

javascript
1function largestOfEach(arr) {
2    let results = [];
3    for (let i = 0; i < arr.length; i++) {
4        let largest = arr[i][0];  // Start with first element
5        for (let j = 1; j < arr[i].length; j++) {
6            if (arr[i][j] > largest) {
7                largest = arr[i][j];
8            }
9        }
10        results.push(largest);
11    }
12    return results;
13}
javascript
1function largestOfEach(arr) {
2    return arr.map(subArray => Math.max(...subArray));
3}
4
5console.log(largestOfEach([[1, 2, 3], [-5, -3, -1], [10, 20]]));
6// [3, -1, 20] ✓

The spread operator ... expands the array into individual arguments. Math.max(...[1, 2, 3]) becomes Math.max(1, 2, 3).

Why Math.max(array) Fails

javascript
1// WRONG: passing an array as a single argument
2Math.max([1, 2, 3]);    // NaN, cannot compare an array object
3
4// RIGHT: spread the array into individual arguments
5Math.max(...[1, 2, 3]); // 3
6
7// ALSO RIGHT: use apply
8Math.max.apply(null, [1, 2, 3]); // 3

Math.max() expects individual number arguments, not an array. Without the spread operator, JavaScript tries to convert the array to a number, which produces NaN.

Fix 4: Using reduce

javascript
1function largestOfEach(arr) {
2    return arr.map(subArray =>
3        subArray.reduce((max, val) => val > max ? val : max, -Infinity)
4    );
5}
6
7console.log(largestOfEach([[4, 1, 9], [-2, -8], [7]]));
8// [9, -2, 7] ✓

Handling Edge Cases

javascript
1function largestOfEach(arr) {
2    return arr.map(subArray => {
3        if (!Array.isArray(subArray) || subArray.length === 0) {
4            return undefined;  // or null, or throw an error
5        }
6        return Math.max(...subArray);
7    });
8}
9
10// Empty sub-array
11console.log(largestOfEach([[1, 2], [], [3]]));
12// [2, undefined, 3]
13
14// Math.max() with no arguments returns -Infinity
15console.log(Math.max());  // -Infinity

Large Sub-Arrays: Stack Overflow with Spread

javascript
1// DANGER: very large arrays cause stack overflow with spread
2const huge = [Array.from({length: 1000000}, (_, i) => i)];
3// Math.max(...huge[0])  // RangeError: Maximum call stack size exceeded
4
5// SAFE: use reduce for large arrays
6function safeMax(arr) {
7    return arr.reduce((max, val) => val > max ? val : max, -Infinity);
8}
9
10function largestOfEach(arr) {
11    return arr.map(subArray => safeMax(subArray));
12}

The spread operator expands arguments on the call stack. Arrays with more than roughly 100,000 elements exceed the stack limit. Use reduce for large datasets.

TypeScript Version

typescript
1function largestOfEach(arr: number[][]): number[] {
2    return arr.map(subArray => {
3        if (subArray.length === 0) {
4            throw new Error("Empty sub-array");
5        }
6        return Math.max(...subArray);
7    });
8}
9
10const result: number[] = largestOfEach([[3, 1, 4], [1, 5, 9], [2, 6]]);
11// [4, 9, 6]

Common Pitfalls

  • Initializing max to 0: Fails for sub-arrays containing only negative numbers. Use -Infinity or the first element instead.
  • Passing an array to Math.max(): Math.max([1,2,3]) returns NaN. Use Math.max(...array) or Math.max.apply(null, array).
  • Stack overflow with spread on large arrays: Math.max(...arr) crashes when arr has more than roughly 100K elements. Use reduce for large data.
  • Not handling empty sub-arrays: Math.max() with no arguments returns -Infinity. Decide whether to return undefined, throw, or return -Infinity.
  • Mutating the original array: Using .sort() to find the max mutates the sub-array. Use Math.max or reduce for non-destructive maximum finding.

Summary

  • Use Math.max(...subArray) with map() for a clean one-liner solution
  • Never initialize a max variable to 0. Use -Infinity or the first element instead
  • Math.max() expects individual numbers, not arrays. Always spread or use apply
  • Use reduce instead of spread for sub-arrays with more than 100K elements
  • Handle edge cases: empty sub-arrays, non-numeric values, and nested irregular structures

Course illustration
Course illustration

All Rights Reserved.