Back to JavaScript
JavaScript
easy
mid

How would you flatten a nested array with custom ordering, top level first then nested?

Walk the array breadth-first: emit all primitives at the current level, queue arrays to process next. This yields top-level values before deeper ones — different from `Array.prototype.flat(Infinity)` which is depth-first. Iterative BFS with a queue is the cleanest implementation.

3 min read·~15 min to think through

Standard flat(Infinity) flattens depth-first — once it descends into a nested array, it emits everything inside before returning. For "top-level first, then nested" we need breadth-first ordering.

Example

js
flattenBFS([1, [2, [3, 4], 5], 6, [7]])
// depth-first (flat):   [1, 2, 3, 4, 5, 6, 7]
// breadth-first (this): [1, 6, 2, 5, 7, 3, 4]

All level-0 primitives, then level-1, then level-2, etc.

Implementation

js
function flattenBFS(arr) {
  const out = [];
  let queue = [arr];
  while (queue.length) {
    const next = [];
    for (const node of queue) {
      for (const item of node) {
        if (Array.isArray(item)) next.push(item);
        else out.push(item);
      }
    }
    queue = next;
  }
  return out;
}

Per level: emit primitives in order, defer arrays. Repeat with the deferred set. O(n) in total elements; O(w) extra space for the queue (w = widest level).

Variant: depth-first with explicit depth control

If the question instead means "flatten to N levels":

js
function flatN(arr, depth = Infinity, out = []) {
  for (const item of arr) {
    if (Array.isArray(item) && depth > 0) flatN(item, depth - 1, out);
    else out.push(item);
  }
  return out;
}

Interview framing

"flat(Infinity) is depth-first. For 'top-level first then nested' I'd do breadth-first: process the current array, emit primitives, queue any sub-arrays for the next pass. O(n) total work, iterative so no stack overflow on deep nests. If they wanted depth-first with a limit instead, that's a recursive walk with a depth counter."

Follow-up questions

  • How does this differ from Array.prototype.flat?
  • How would you handle objects that look like arrays (array-like)?
  • Iterative vs recursive — when does it matter?

Common mistakes

  • Using flat(Infinity) — that's depth-first.
  • Recursing deeply on adversarial input → stack overflow.
  • Mutating the input.

Performance considerations

  • Iterative BFS avoids stack overflow on deeply nested input. Avoid concat/spread in hot loops — push individual items.

Edge cases

  • Empty arrays and empty input.
  • Sparse arrays (holes).
  • Cycles — pass a visited Set if the input may self-reference.

Real-world examples

  • Tree → list rendering with level-priority, comment threads, file tree breadcrumbs.

Senior engineer discussion

Seniors clarify ordering semantics with the interviewer first (DFS vs BFS), mention `flat(Infinity)`'s actual behavior, and discuss generators if memory is a concern — yielding lazily lets a consumer stop early.

Related questions