skip to content

In JavaScript, what is the difference between Array.prototype.map and Array.prototype.filter, and what does each one return?

level: juniorimportance: must knowfreq 82%

answer

  1. one transforms, one selects
  2. same length versus shorter
  3. return value becomes element or test
  4. braced arrow body returns nothing

basics

~20 s

Array.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 s

Both 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 lines
javascript
const 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 untouched

go deeper

for a junior

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.

for a middle

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.

for a senior

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.

for a principal

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

context