In JavaScript, what is the difference between Array.prototype.map and Array.prototype.filter, and what does each one return?
answer
- one transforms, one selects
- same length versus shorter
- return value becomes element or test
- braced arrow body returns nothing
basics
~20 sArray.prototype.map builds a new array of the same length by replacing each element with whatever its callback returns. Array.prototype.filter builds a new array holding only the elements whose callback returned a truthy value. Neither changes the original array.
solid answer
~40 sBoth take a callback invoked as `(element, index, array)` and both return a brand-new array, leaving the source alone. The difference is what the callback's return value means. For `map`, the returned value *becomes* the element, so the result always has the same length as the source — a transformation. For `filter`, the returned value is only tested for truthiness and is then thrown away; the original element is kept or dropped, so the result has the same elements but a length between zero and the source length — a selection. The classic beginner bug is a braced arrow body in `map`: `[1,2,3].map(n => { n * 2 })` has no `return`, so every callback yields `undefined` and you get `[undefined, undefined, undefined]`.
code
javascript · 10 linesconst users = [
{ name: 'ada', active: true },
{ name: 'linus', active: false },
{ name: 'grace', active: true },
];
console.log(users.map(u => u.name)); // ['ada', 'linus', 'grace']
console.log(users.filter(u => u.active).length); // 2
console.log([1, 2, 3].map(n => { n * 2 })); // [undefined, undefined, undefined]
console.log(users.length); // 3 — source untouchedgo deeper
Be able to say in one breath that map transforms every element into a new array of the same length, and filter keeps a subset, and that both return a new array. Recognise the braced-arrow bug that yields an array of undefined.
Explain the mechanics: the callback receives element, index and array; map writes the return value into the slot while filter only coerces it to a boolean and keeps the original element. Note that the resulting copy is shallow.
Show production judgment: keep callbacks pure so a map does not quietly mutate shared objects, and justify chain order by what the predicate needs to see, not just by element count.
Own the tradeoff between an expressive chain and a single pass over large data, and set the team convention for where declarative pipelines are the default and where intermediate-array allocation makes a hand-written pass worth its cost.
## The two methods side by side `Array.prototype.map` and `Array.prototype.filter` are the two most-used array transformation methods in JavaScript. Both are *higher-order*: you hand them a function (the callback) and they call it once per element. Both call the callback with three arguments — the element, its index, and the array being traversed — and both accept an optional second argument that becomes `this` inside a non-arrow callback. Both return a **new** array and never reassign elements of the source. The whole difference is in how each method interprets the callback's return value. ```js const nums = [1, 2, 3, 4]; nums.map(n => n * 2); // [2, 4, 6, 8] — same length, new values nums.filter(n => n > 2); // [3, 4] — original values, fewer of them nums; // [1, 2, 3, 4] — untouched ``` ## map: the return value becomes the element `map` treats the callback as a *transformer*. Whatever the callback returns is written into the corresponding slot of the result. Because there is exactly one output slot per input element, `result.length === source.length` always holds for `map` — there is no way for a `map` callback to drop an element. That one-to-one guarantee is why the missing-`return` bug is so common. An arrow function with an expression body returns the expression implicitly; an arrow function with a *braced* body does not. ```js [1, 2, 3].map(n => n * 2); // [2, 4, 6] [1, 2, 3].map(n => { n * 2 }); // [undefined, undefined, undefined] [1, 2, 3].map(n => ({ v: n })); // [{v:1},{v:2},{v:3}] — parens, or the braces read as a block ``` Returning an object literal from a concise arrow body needs the parentheses shown above, otherwise `{` starts a block statement rather than an object. ## filter: the return value is only a test `filter` treats the callback as a *predicate*. It coerces the returned value with the same truthiness rules the language uses everywhere (`0`, `-0`, `0n`, `""`, `null`, `undefined`, `NaN` and `false` are falsy; everything else is truthy) and then keeps or drops the **original** element. The callback's return value never appears in the output. ```js [0, 1, 2, 3].filter(n => n % 2); // [1, 3] — 1 is truthy, 0 is falsy ['a', '', 'b', null].filter(Boolean); // ['a', 'b'] — the idiomatic "drop falsy" trick ``` Because only truthiness matters, a predicate does not have to return a real boolean, though returning one is clearer. `filter(Boolean)` works precisely because `Boolean` used as a one-argument function is exactly the truthiness test. ## Neither method mutates the source array Both are copying methods: they allocate a new array and leave the receiver's contents alone. The copy is *shallow*, though — if the elements are objects, the new array holds the same object references, so mutating `result[0].name` is visible through the original array too. A `map` callback that mutates the object it is handed is a side effect the method itself cannot prevent, and it is a frequent source of confusing bugs; keep the callback pure and return a new value instead. ## Order matters when you chain them These methods return arrays, so they chain. Two rules of thumb: ```js users.filter(u => u.active).map(u => u.name); // fewer transformations users.map(u => u.name).filter(Boolean); // transform first, then test the result ``` Putting `filter` first means `map` runs on fewer elements. But the order is not merely a performance choice: the two orders test different things. In the first line the predicate sees the whole user object; in the second it sees only the produced name. Swap them blindly and the predicate may no longer have the property it needs. ## What interviewers listen for A confident answer names the return type of each method, states the length relationship (`map` preserves it, `filter` cannot grow it), says that both are non-mutating, and mentions the truthiness rule for the predicate. Mentioning the braced-arrow `undefined` trap unprompted signals you have actually debugged this in real code.
- Why does an arrow callback with curly braces so often break a map call?A concise arrow body (`n => n * 2`) returns its expression implicitly; adding braces turns the body into a statement block, so the function returns `undefined` unless you write an explicit `return`. Since `map` writes the callback's return value into every slot, the result is an array of `undefined` of the right length — no error, just silently wrong data.
- Does a filter predicate have to return an actual boolean?No. `filter` applies the language's truthiness coercion to whatever comes back, so `n => n % 2` works: `1` is truthy, `0` is falsy. That is also why `array.filter(Boolean)` is the idiomatic way to drop `null`, `undefined`, `''`, `0` and `NaN`. Returning a real boolean is still clearer for readers, and avoids surprises when the expression can produce an object.
- In a chain, does it matter whether filter or map comes first?Yes, in two ways. Filtering first means the transformation runs on fewer elements. More importantly the predicate sees different data: before `map` it sees the source element, after `map` it sees the produced value. If the test needs a property that `map` discards, the filter must come first — the orders are not interchangeable refactors.
saying these in an interview costs you the question
- Says map can skip elements by returning nothing
- Claims map and filter mutate the original array
- Thinks filter's callback return value ends up in the result
- Believes a predicate must return true or false exactly
- Assumes map's result may be shorter than the source