Big O Notation (in Everyday Code)

A way to describe how a piece of code's cost grows as its input grows — most usefully spotted as a loop nested inside another loop over related data.

Purpose

Big O notation describes how a piece of code’s cost (time, usually) grows as its input size grows — not an exact measurement, but a growth-rate category. For everyday code, the most valuable skill is recognizing the shape that produces bad growth, not formal analysis.

The shape to recognize

// O(n²) — for each of n orders, .find() scans up to m customers
function attachCustomerNames(orders, customers) {
  return orders.map((order) => ({
    ...order,
    customerName: customers.find((c) => c.id === order.customerId)?.name,
  }));
}

// O(n + m) — a lookup built once, then each order is an O(1) lookup
function attachCustomerNamesFast(orders, customers) {
  const byId = new Map(customers.map((c) => [c.id, c.name]));
  return orders.map((order) => ({ ...order, customerName: byId.get(order.customerId) }));
}

Common classes, informally

  • O(1) — constant: a Map/object lookup, an array index access.
  • O(n) — linear: a single pass over an array (map, filter, forEach, a single for loop).
  • O(n log n) — typical of a good sort (Array.prototype.sort’s underlying algorithm).
  • O(n²) — quadratic: a loop nested inside another loop over related-sized data — often hiding inside innocent-looking chained array method calls, as above.

Behavior notes

  • The fix for the O(n²) shape above is almost always the same: build a Map/object lookup once (O(n) or O(m) to build), turning each subsequent lookup into O(1) instead of an O(m) scan.
  • This only matters at meaningful scale — both versions above run indistinguishably fast for a handful of items; the difference appears as input size grows into the thousands or more.
  • Complexity is about growth rate, not a guarantee about absolute speed — a “worse” complexity class can still be faster in practice for small enough inputs, due to constant-factor overhead.

Common mistakes

  • Optimizing complexity for code that never runs on meaningfully large input, adding complexity for no real benefit — recall “measure before optimizing” from Performance and Diagnostics.
  • Missing the O(n²) shape specifically because it’s spread across chained, individually innocent-looking array method calls rather than an obvious nested for loop.

Runtime notes

A property of the algorithm itself, not the runtime — the growth-rate behavior is identical regardless of which JavaScript engine runs the code.

Taught in these lessons

Updated 2026-09-03