Lesson 32 of 80
flat() collapses nested arrays by depth; flatMap() fuses map and flatten into one pass. Small methods, but they replace a surprising amount of manual loop code.
12 min read
Every option explains itself — why the right one is right, and why each wrong one is wrong. Miss one and it comes back later on a spaced-repetition schedule.
This topic is about judgment and practical tradeoffs more than a single right answer — a mock interview is the better way to test yourself on it.
Be interviewed on this →Hints without spoilers, and a plain explanation of why a wrong answer was wrong — answered only from this lesson. Free account, no card.
Sign up freeWhat interviewers actually ask about this, and what a strong answer has to cover.
A strong answer covers
Likely follow-ups
.flat(depth) takes an array containing nested arrays and flattens it into a single array, up to depth levels deep. .flat() with no argument defaults to depth 1.
.flatMap() does a .map() and then flattens the result by exactly one level, in a single pass. It exists because "map a function that returns an array, then flatten" is an extremely common pattern — filtering-while-mapping, splitting one item into many, or dropping items entirely — and doing it as two separate array traversals is both slower and more code.
const nested = [1, [2, 3], [4, [5, 6]]];
nested.flat(); // [1, 2, 3, 4, [5, 6]] -- only one level deep
nested.flat(2); // [1, 2, 3, 4, 5, 6] -- two levels
nested.flat(Infinity); // flattens fully, any depth
Now the real win — flatMap() over .map().flat(). Say you have a list of sentences and want every word, but you also want to silently drop empty strings from double spaces:
const sentences = ["hello world", "goodbye moon", ""];
// map + flat: two full array passes, and empty strings survive as [""]
sentences
.map((s) => s.split(" ").filter(Boolean))
.flat();
// flatMap: one pass, same result, reads as one operation
sentences.flatMap((s) => s.split(" ").filter(Boolean));
// ["hello", "world", "goodbye", "moon"]
The bigger, less obvious use is map that can also filter or expand, using the fact that returning [] for an item removes it and returning multiple items expands it:
const orders = [
{ id: 1, items: ["pen", "notebook"] },
{ id: 2, items: [] },
{ id: 3, items: ["mug"] },
];
// one line, no separate filter step, no intermediate array of arrays
const allItems = orders.flatMap((o) => o.items);
// ["pen", "notebook", "mug"]
Doing this with .map() alone would leave you with [["pen", "notebook"], [], ["mug"]] — a shape you'd then have to flatten manually. flatMap() collapses "transform each item into zero, one, or many items" into a single readable call.
flat() with no argument only goes one level deep, not all the way. [1, [2, [3, [4]]]].flat() still leaves [3, [4]] nested. Use .flat(Infinity) when you genuinely don't know or care how deep the nesting goes.flatMap()'s "return an array to expand, empty array to remove" trick reads as magic if you haven't seen it before, but it's just consistent flattening. Returning [] for an item is the same rule as returning [x, y] for another — both get flattened into the result, one just contributes zero elements.flat() and flatMap() skip holes in sparse arrays, like most other array methods. [1, , 3].flat() gives [1, 3], not [1, undefined, 3] — the hole itself, not just its value, is skipped.flatMap() is the array-shaped instance of a pattern known more generally as "bind" or "chain" in functional programming — take a value, apply a function that itself produces a structure of the same shape, and merge the results instead of nesting them. You've already met the "function that returns a transformed value" half of this idea in the higher-order-functions lesson; flatMap() is what happens when that returned value is itself a collection you want merged in, not boxed up.