Algorithms#

Most everyday algorithmic work uses methods on Array (and friends) plus a few small helpers.

Sorting#

Array.prototype.sort is in-place and stable (since ES2019). The default comparator coerces to strings, so always pass one for numbers.

const xs = [10, 2, 33, 4];
xs.sort();                          // ["10","2","33","4"] order
xs.sort((a, b) => a - b);           // [2, 4, 10, 33]

people.sort((a, b) => a.age - b.age);

For an immutable sort, use toSorted (ES2023) or [...xs].sort(cmp).

Map / Filter / Reduce#

const total = orders
  .filter((o) => o.status === "paid")
  .map((o) => o.amount)
  .reduce((a, b) => a + b, 0);

const byCustomer = orders.reduce((acc, o) => {
  (acc[o.customerId] ??= []).push(o);
  return acc;
}, {});

Recursion#

function fact(n) {
  return n <= 1 ? 1 : n * fact(n - 1);
}

For repeated work, memoise.

function memoize(fn) {
  const cache = new Map();
  return (...args) => {
    const key = JSON.stringify(args);
    if (!cache.has(key)) cache.set(key, fn(...args));
    return cache.get(key);
  };
}

Linked List#

class Node {
  constructor(value, next = null) {
    this.value = value;
    this.next  = next;
  }
}

function push(head, value) {
  return new Node(value, head);
}

Concurrency#

JavaScript is single-threaded, but I/O is concurrent. Use Promise.all for parallel awaits, Promise.allSettled to keep going past failures, and Promise.race for “first wins”.

const responses = await Promise.all(urls.map((u) => fetch(u)));
const bodies    = await Promise.all(responses.map((r) => r.text()));