All questions

Deep Omit

Premium

Deep Omit

Implement deepOmit(value, keys) — return a copy of value with the given keys stripped from every nested object at every depth. This is the recursive cousin of lodash's omit: instead of dropping keys only at the top level, you walk the whole tree and drop the same forbidden names wherever they appear. The common use case is sanitising a payload before you log it or send it across the wire — strip password, token, and secret once, anywhere they sit in a nested config, response, or audit blob.

Signature

// Returns a structural copy of `value` with every occurrence of any key in
// `keys` removed — at any depth. Arrays are walked; their items keep their
// own keys filtered. Primitives are returned as-is. Input is never mutated.
function deepOmit<T>(value: T, keys: string[]): T;

Examples

// Omit a single key at the top level.
deepOmit({ a: 1, b: 2 }, ['a']);
// → { b: 2 }
// Omit the same key from every depth at once.
deepOmit({ a: 1, b: { a: 2, c: 3 } }, ['a']);
// → { b: { c: 3 } }
//   `a` is gone from BOTH levels — including the nested object.
// Walk into arrays; their item objects still get filtered.
deepOmit({ users: [{ id: 1, password: 'x' }, { id: 2, password: 'y' }] }, ['password']);
// → { users: [{ id: 1 }, { id: 2 }] }
//   The array stays an array; only the inner objects lose `password`.

Notes

  • Recursion is the shape. Walk every key of every plain object. Recurse into the value you keep; skip the value of any key in keys.
  • Arrays don't have keys to omit, but their items might. Array.isArray(value) is your branch: for an array, map each element through deepOmit so the recursion continues into nested objects.
  • Leaves return as-is. Primitives (number, string, boolean, null, undefined), Date, RegExp, Map, Set, and anything else that isn't a plain {} or [] is opaque — return it without copying. The shape of the tree is {} | [] | leaf.
  • Never mutate the input. Allocate fresh {} or [] at every level you descend into; the caller's original tree must look identical after the call.
  • keys is an array of strings. Symbol keys are out of scope — skipping them is acceptable. Use a Set for the lookup so the per-key check is O(1), not O(n).
  • A non-existent key is a no-op. deepOmit({ a: 1 }, ['nope']) returns { a: 1 }. No throwing, no warning.

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