Arrays

Write JavaScript loops using map, filter, reduce and find

Learn how to replace JavaScript loops with a functional approach using map(), filter(), reduce(), and find() to process arrays declaratively, not imperatively.

Loops iterate arrays to perform work on each element. You can write that work imperatively with for or for...of, or declaratively with array methods.

NOTE: I don’t recommend one approach over the other. I just want to introduce different ways to perform the same thing and maybe introduce you to new functions which you might have never used until now.

map, filter, reduce, find

These four methods cover most array processing:

  • map returns an array with the same length
  • filter returns an array with fewer items than the original
  • reduce returns a single value (or object)
  • find returns the first item that satisfies a condition

map, filter, and reduce arrived in ES5. find arrived in ES2015.

If you’re ever unsure which array method fits your use case, try the array method chooser.

Declarative code describes what should happen. Imperative code spells out every step.

Execute something on every element with map

A loop version:

const performSomething = (item) => {
  //...
  return item
}
const items = ['a', 'b', 'c']
items.forEach((item) => {
  performSomething(item)
})

With map, JavaScript builds a new array from the return value of your function:

const items = ['a', 'b', 'c']
const newArray = items.map((item) => performSomething(item))

This generates a new array, without editing the original one (what we call immutability)

Since we use a single function in the map callback function, we can rewrite the sample as:

const items = ['a', 'b', 'c']
const newArray = items.map(performSomething)

Finding a single element in the array

Sometimes you need to look for a specific item in the array, and return it.

This is how you would do so with a loop:

const items = [
  { name: 'a', content: { /* ... */ }},
  { name: 'b', content: { /* ... */ }},
  { name: 'c', content: { /* ... */ }}
]
for (const item of items) {
  if (item.name === 'b') {
    return item
  }
}

Here is the non-loop version, using find() (ES6+):

const b = items.find((item) => item.name === 'b')

Here is the same functionality using filter() (ES5+):

const b = items.filter((item) => item.name === 'b').shift()

shift() returns the first item in the array without raising an error if the array is empty (returns undefined in that case).

Note: shift() mutates the array, but the array it mutates is the one returned by filter(), not the original array. If this sounds unacceptable, you can check if the array is not empty and get the first item using b[0].

For learning purposes (does not make much sense in practice), here is the same functionality using reduce():

const items = [
  { name: 'a', content: { /* ... */ }},
  { name: 'b', content: { /* ... */ }},
  { name: 'c', content: { /* ... */ }}
]

const b = items.reduce((result, item) => {
  if (item.name === 'b') { result = item }
  return result
}, null)

filter() and reduce() will iterate over all the array items, while find() will be faster.

Iterate over an array to count a property of each item

Use reduce() to get a single value out of an array. For example sum the items content.value property:

const items = [
  { name: 'a', content: { value: 1 }},
  { name: 'b', content: { value: 2 }},
  { name: 'c', content: { value: 3 }}
]

using a loop:

let count = 0
for (const item of items) {
  count += item.content.value
}

can written as

const count = items.reduce((result, { content: { value } }) => result + value, 0)

Map [1, 2, 3] to doubled values, filter to keep only values above 2, then reduce to a sum. Log each step.

Lesson completed