ESC

Type to search the knowledge base.

Functional Array Patterns

Practical functional array techniques — flatMap, partitioning, indexing, zip, and when chaining hurts performance.

beginner3 min read
  • javascript
  • arrays
  • functional
  • patterns

Beyond map/filter/reduce, a small set of patterns shows up constantly in UI data shaping. Knowing them by name makes code reviews shorter and interview whiteboards less blank.

flatMap for map + flatten

const users = [
  { name: 'Ada', tags: ['js', 'wasm'] },
  { name: 'Grace', tags: ['cobol'] },
];

users.flatMap((u) => u.tags);
// ['js', 'wasm', 'cobol']

// conditional emit zero or more
[1, 2, 3, 4].flatMap((n) => (n % 2 === 0 ? [n, n * 10] : []));
// [2, 20, 4, 40]

Prefer flatMap over map(...).flat().

Partition

function partition(arr, pred) {
  return arr.reduce(
    ([pass, fail], x) => {
      (pred(x) ? pass : fail).push(x);
      return [pass, fail];
    },
    [[], []],
  );
}

const [active, inactive] = partition(users, (u) => u.active);

One pass instead of two filters.

Index by key

function indexBy(arr, keyFn) {
  return arr.reduce((map, item) => {
    map.set(keyFn(item), item);
    return map;
  }, new Map());
}

const byId = indexBy(users, (u) => u.id);
byId.get(3);

Map keeps insertion order and any key type; plain objects coerce keys to strings.

Group by

function groupBy(arr, keyFn) {
  return arr.reduce((acc, item) => {
    const k = keyFn(item);
    if (!acc.has(k)) acc.set(k, []);
    acc.get(k).push(item);
    return acc;
  }, new Map());
}

Unique

const unique = [...new Set(ids)];

// unique by key
function uniqueBy(arr, keyFn) {
  const seen = new Set();
  return arr.filter((item) => {
    const k = keyFn(item);
    if (seen.has(k)) return false;
    seen.add(k);
    return true;
  });
}

Zip and chunk

function zip(a, b) {
  const n = Math.min(a.length, b.length);
  return Array.from({ length: n }, (_, i) => [a[i], b[i]]);
}

function chunk(arr, size) {
  const out = [];
  for (let i = 0; i < arr.length; i += size) {
    out.push(arr.slice(i, i + size));
  }
  return out;
}

chunk([1, 2, 3, 4, 5], 2); // [[1,2],[3,4],[5]]

Sort without mutate

const sorted = [...items].sort((a, b) => a.score - b.score);
// or items.toSorted(...) in modern engines

sort mutates — copy first (see sorting arrays correctly).

Chaining vs one pass

// readable, intermediate arrays
arr.filter(isActive).map(toDto).slice(0, 20);

// hot path
const out = [];
for (const x of arr) {
  if (!isActive(x)) continue;
  out.push(toDto(x));
  if (out.length === 20) break;
}

Default to clarity; fuse loops when profiling says so.

Interview answer

“I use flatMap for nested lists, partition/groupBy/indexBy as reduce patterns, Set for uniqueness, and copy-before-sort. I chain map/filter for small UI data and collapse to one loop for large hot paths. Map beats object for non-string keys.”

window sliding and pairs

function* windows(arr, size) {
  for (let i = 0; i <= arr.length - size; i++) {
    yield arr.slice(i, i + size);
  }
}

function pairs(arr) {
  return arr.slice(0, -1).map((x, i) => [x, arr[i + 1]]);
}

// sum of deltas
pairs(prices).map(([a, b]) => b - a);

These show up in charting and form wizards (“steps”). Keep helpers pure and generic so UI code reads as data flow. If a helper is used once, an inline loop is fine — extract when the name clarifies intent (partition, indexBy, uniqueBy).

Further reading

Related guides