Skip to content
DSA in JavaScript
Foundations

2D Arrays (Matrices)

Fundamentals · Core

Level: CoreMatrix Traversal

Complexity

OperationTime
Read or write a cellO(1)
Visit every cellO(rows × cols)
Transpose or rotateO(rows × cols)
Search in a sorted matrixO(rows + cols) with the staircase method

A matrix is an array of rows, grid[row][col]. Grids model boards, maps, images and dynamic programming tables, so grid traversal is one of the most reused skills.

Basics

const rows = grid.length;
const cols = grid[0].length;
const value = grid[r][c];

Create one correctly. Array(rows).fill(Array(cols).fill(0)) reuses the same row for every index.

const grid = Array.from({ length: rows }, () => Array(cols).fill(0));

Moving around

Store the four directions once and loop over them. Check bounds before reading.

const DIRS = [[1, 0], [-1, 0], [0, 1], [0, -1]];
for (const [dr, dc] of DIRS) {
  const nr = r + dr, nc = c + dc;
  if (nr < 0 || nc < 0 || nr >= rows || nc >= cols) continue;
  // use grid[nr][nc]
}

Add the four diagonals for 8-direction movement.

Handy facts

  • Cells on the same diagonal share r - c. Cells on the same anti-diagonal share r + c.
  • A flat index and a cell convert with i = r * cols + c, r = Math.floor(i / cols), c = i % cols.
  • Transpose swaps grid[r][c] with grid[c][r]. Rotating 90 degrees clockwise is a transpose then reversing each row.

Common patterns

  • Flood fill, islands, regions: DFS or BFS from a cell (see Matrix Traversal pattern).
  • Shortest path in a maze: BFS.
  • Spiral and rotation: track four boundaries and shrink them.
  • Grid DP: dp[r][c] from dp[r-1][c] and dp[r][c-1] (unique paths, minimum path sum).

Common mistakes

  • Shared rows from fill (see above).
  • Mixing up rows and cols, or grid[x][y] against grid[y][x].
  • Mutating the grid to mark visited and then needing the original.

Code

matrix.js
export const createGrid = (rows, cols, fill = 0) => Array.from({ length: rows }, () => Array(cols).fill(fill));

export const transpose = m => m[0].map((_, c) => m.map(row => row[c]));

export const rotateClockwise = m => transpose(m).map(row => row.reverse());

export function spiral(m) {
  const out = [];
  let top = 0, bottom = m.length - 1, left = 0, right = m[0].length - 1;
  while (top <= bottom && left <= right) {
    for (let c = left; c <= right; c++) out.push(m[top][c]);
    top++;
    for (let r = top; r <= bottom; r++) out.push(m[r][right]);
    right--;
    if (top <= bottom) { for (let c = right; c >= left; c--) out.push(m[bottom][c]); bottom--; }
    if (left <= right) { for (let r = bottom; r >= top; r--) out.push(m[r][left]); left++; }
  }
  return out;
}

const DIRS = [[1, 0], [-1, 0], [0, 1], [0, -1]];

export function countIslands(grid) { // grid of '1' (land) and '0'
  let count = 0;
  const sink = (r, c) => {
    if (grid[r]?.[c] !== '1') return;
    grid[r][c] = '0';
    for (const [dr, dc] of DIRS) sink(r + dr, c + dc);
  };
  grid.forEach((row, r) => row.forEach((v, c) => { if (v === '1') { count++; sink(r, c); } }));
  return count;
}

Practice

Spotted a mistake or something unclear? Report it on GitHub.

On this page