All questions

Deep Map

Premium

Deep Map

Implement deepMap(value, transform) — walk an arbitrarily nested structure (arrays and plain objects) and apply transform to every leaf value, returning a new structure with the same shape but with each leaf replaced by transform(leaf). A leaf is anything that is not a plain object or array: numbers, strings, booleans, null, undefined, Date, functions, class instances — they all reach transform untouched. Think of it as Array.prototype.map generalised across heterogeneous trees instead of a flat list.

Signature

// Returns a NEW value with the same shape as `value`, where every leaf
// has been replaced by transform(leaf). Containers (arrays and plain
// objects) are recreated fresh; nothing in the result aliases the input.
function deepMap<T>(value: T, transform: (leaf: unknown) => unknown): T;

Examples

// Flat object — every value goes through transform.
deepMap({ a: 1, b: 2, c: 3 }, (x) => x * 10);
// → { a: 10, b: 20, c: 30 }
// Nested object — the recursion descends; only the LEAVES are mapped.
deepMap({ user: { name: 'ada', age: 30 } }, (x) =>
  typeof x === 'string' ? x.toUpperCase() : x,
);
// → { user: { name: 'ADA', age: 30 } }
// Array of primitives.
deepMap([1, 2, 3, 4], (x) => x * x);
// → [1, 4, 9, 16]
// Mixed — object containing arrays containing objects.
deepMap(
  { items: [{ qty: 1 }, { qty: 2 }], total: 3 },
  (x) => (typeof x === 'number' ? x + 100 : x),
);
// → { items: [{ qty: 101 }, { qty: 102 }], total: 103 }

Notes

  • Leaf vs container. A container is an array (Array.isArray) or a plain object literal. Everything else — primitives, Date, Map, functions, class instances — is a leaf. transform is called on the leaf as a whole; you never recurse into a Date.
  • Shape is preserved. Same keys, same array lengths, same nesting depth. Only leaf values change.
  • Do not mutate the input. Allocate a new container at every level you descend into. The top-level result, and every nested container, must be a fresh reference.
  • null is a leaf. typeof null === 'object', but null has no properties — pass it to transform rather than treating it like a container.
  • Date and other class instances are leaves. Even though typeof new Date() === 'object', you do not recurse into its internals; transform receives the Date itself.
  • Cycles are out of scope for the base spec — the input is assumed to be a finite tree. See Going further in the solution for a WeakMap-based extension.

Unlock the solution & editor

  • Runnable editor + tests

    Solve in the browser with instant Jest feedback.

  • Detailed solutions

    Walkthroughs, edge cases, and complexity notes.

  • Multi-framework variants

    React, Vue, Vanilla, Angular — same question, different stacks.

Upgrade to Premium