little cubes

When to use each JavaScript iteration method

And a practical guide to the reduce() function

There are three primary methods to iterate over an array in JavaScript: forEach, map, and reduce. Each of these functions has its own use case, and it’s important to understand the differences between them.

forEachSection titled: forEach

The forEach method is the most basic of the three. It performs a simple iteration over an array, and doesn’t return anything. As it’s name implies, it is mostly equivalent to a for…of loop in JavaScript.

Use forEach when:

  • You just want to loop over the array
  • Your callback function doesn’t return anything
  • When you want to mutate (i.e. change something inside of) objects in the array
    • e.g. array.forEach((item) => item.foo = 'bar')

DO NOT USE forEach WHEN:

  • You want to await one or more promises
    • forEach is synchronous, so it will not wait for the async function to finish before moving on to the next iteration (making the callback function async will not help)
    • You have to decide whether you want to wait for the promises in sequence (one at a time), or in parallel (all at the same time).
      • If you want to wait for the promises in sequence, use a for…of loop.
        for (const item of items) {
        const result = await myAsyncFunction(item)
        }
      • If you want to wait for the promises in parallel, use map() to generate an array of promises, and then pass that to Promise.all()
        const promises = items.map((item) => myAsyncFunction(item))
        const results = await Promise.all(promises)

mapSection titled: map

The map method transforms each element in the array. Its only argument is a callback function that takes the original array item and outputs a different array item.

map returns a new array with the same length as the original array, but with each element transformed.

const strings = ['0', '1', '2', '3', '4']
const numbers = strings.map((str) => parseInt(str)) // [0, 1, 2, 3, 4]

Unlike forEach, the map callback function can be async. Doing so will make map return an array of Promises, which you can then process in parallel (all at the same time) by passing the array returned from map to await Promise.all(). If you want to process the Promises in sequence (one at a time), you can await them inside of a for...of loop instead, and manually push the results into a new array.

// Process async operations in parallel
const promises = items.map((item) => myAsyncFunction(item))
const results = await Promise.all(promises)
// Process async operations in sequence
const results = []
for (const item of items) {
const result = await myAsyncFunction(item)
results.push(result)
}

Use map when:

  • You want to take each item in the array and transform it in some way
  • You want to process a batch of async functions in parallel (all at the same time)

DO NOT USE map WHEN:

  • You just want to iterate over the array. A simple iteration should be performed by forEach (or a for loop), not map.
  • You want to process a batch of async functions in sequence (one at a time)

reduceSection titled: reduce

The reduce method takes an array and reduces it to a single value. That single value is usually either an object or a number. Reduce tends to confuse people more than the other two, but 99% of the time reduce is used for one of two tasks.

Use reduce when:

  1. You want to sum up some numeric value contained in each item in the array
    • This is happening when 0 is passed as the final argument to reduce
  2. You want to transform the array into an object; indexing each item by some property
    • This is happening when {} is passed as the final argument to reduce

The signature of reduce can also be intimidating because it usually has a big inline function, but without the inline function it just looks like this:

reduce(callback, initialValue)

The callback function is called once for each item in the array. It takes two arguments: accumulator and currentValue.

  • accumulator: Remember before when we said that reduce() reduces an array to a “single value”? That single value is the accumulator. Every iteration of the callback function has to return something. Whatever you return from the callback will be passed in as the accumulator for the next iteration of the callback function. That’s how it “accumulates”.

  • currentValue is just the current item in the array. It’s common to change the name of this argument to something more descriptive, specific to your situation.

Example of (1): summing up a numeric valueSection titled: Example of (1): summing up a numeric value

First, here is the classic example of using reduce to sum up an array of numbers:

const numbers = [1, 2, 3, 4, 5]
function add(a, b) {
return a + b
}
const sum = numbers.reduce(add, 0) // 15

But it’s not often you have an array of numbers. It’s usually an array of objects where each object has some numeric value that you want to sum up.

const arr = [
{id: 'foo', value: 1},
{id: 'bar', value: 2},
{id: 'baz', value: 3},
]
const sum = arr.reduce((sum, item) => sum + item.value, 0)

It looks a little more complicated this time because of the inline function, but the only difference is that instead of just a number, we have an object with a numeric value property.

Example of (2): transforming an array into an objectSection titled: Example of (2): transforming an array into an object

Finding an item in an array is an O(n) operation. If you have an array of 100 items, it could take 100 iterations to find the item you’re looking for. For that reason, if you’re going to be doing a lot of searching, it’s common to transform an array into an object, indexed by your search value, so that you can find the value immediately (in constant, O(1), time), without going through the whole array.

Let’s transform this array into this object, so that we can search for each item by it’s id.

const array = [
{id: 'foo', value: 1},
{id: 'bar', value: 2},
{id: 'baz', value: 3},
]
const object = {
foo: {id: 'foo', value: 1},
bar: {id: 'bar', value: 2},
baz: {id: 'baz', value: 3},
}

And that can be done with reduce like this:

const object = array.reduce((accum, item) => {
accum[item.id] = item
return accum
}, {})