As a javascript developer, no matter frontend or backend. We all have to manipulate with data. And usually, this data sent to us in lists (arrays ). So in this article, I’ll try to cover basic methods to work with lists. Such as forEach map and reduce .

But let’s start with callback

All array methods (not only mentioned in this article, with a little exception for reduce ) accepting the same parameter, so it makes sense first spend a few minutes to explain how it actually works.

callback it is a function called on each item in the array.

function callback(element, index, array) {}

It has such parameters:

element — the current element being processed in the array.

— the current element being processed in the array. index — the index of the current element being processed in the array.

— the index of the current element being processed in the array. array — the array which we iterate through (always the same)

forEach

The most basic and in my opinion the most useless method in this article. You can’t imagine how many times I’ve seen needless usage of forEach which could have been substituted with one of the methods which I’m going to mention below and in further articles.

Basically, all that it does, it’s iterating through an array. And why it’s useless? Because previously forEach was the only way, how to iterate through an array. So it was used as a base to solving different problems. But now we have many different methods (you can think as high-level methods) which will most likely suit you more.

Use cases:

Logging:

The perfect case, it’s when you need to iterate through an array to log values somewhere. For instance, error tracking services.

const errors // list of errors // log errors to your error tracking service

errors.forEach(err => trackError(err))

map

map is my favorite one. You need it whenever you want to modify the array or get some specific values from it. The main benefit is that it takes an array and return a new one from the result of a callback function leaving the original one unchanged. It’s mean that you can still use the original array for other needs.

Use cases:

Get specific value from an array

Imagine the case we have a list of passengers object and we want to return a list of passenger names to display it somewhere on our website:

// passenger is an Object which has such shape:

// { name: string, surname: string, ...someotherFields } passengers.map(p => `${p.name} ${p.surname}`) // from - [

// {name: 'John', surname: 'Snow'},

// { name: 'Daenerys', surname: 'Targaryen'}

// ]

//

// to ['John Snow', 'Daenerys Targaryen']

Modify array

Sometimes we want to modify data, which we received from the backend. For instance, we’re using lastName in our code, but backend send us surname .

passenger.map(({ surname, ...p }) => ({...p, lastName: surname }))

filter

As you can guess, by the name filter main and most likely only one purpose it to filter arrays. It’s taking an original array and returns a new array with items for which callback returns a truthy value. And the same as map it keeps the original array unchanged.

Use case:

Filter by some value:

We need to show a list of passenger older than 18 years old.

const grownupPassengers = passengers.map(p => p.age >= 18)

Filter out falsy values:

Sometimes backend can return us null instead of some items. We can remove them with filter

const maybePassengers =

[{ proper passenger }, null, { proper passenger }] const passengers = maybePassengers.filter(Boolean) // become: [{ passenger }, { passenger }]

Implement search:

We can implement search by filtering items which contain the search value

const search = 'Jo' const filteredPassengers = passengersFullNames.filter(

p => p.includes(search)

)

Combining methods

The great feature of array methods which returns new array is that you can combine them together.

For instance, you can filter passengers by age and then display their names:

// passengers

passengers

.filter(p => p.age > 18)

.map(p => `${p.name} ${p.surname}`

You can see how declarative this code feels and how easy to reason about what actually happens in this code

reduce

The most complex and interesting one. The “Swith Knife” of array methods. Unlike the other two methods in this article, reduce return not array, but accumulator value and has a bit different structure.

arr.reduce(callback[, initialValue])

initialValue — initial value of accumulator (see below). If no initial value is supplied, the first element in the array will be used. Calling reduce on an empty array without an initial value is an error. So I’d recommend to always supply it if you not sure if an array is empty or not

function callback(accumulator, element, index, array) {}

as you can see everything is the same except accumulator — which is either previously returned value in the last invocation of the callback, or initialValue , if supplied. accumulator can be any value and really depends on what you’re trying to achieve.

The easiest way to understand reduce , in my opinion, is to check some use cases.

Use cases:

Get the total sum of the list of transactions:

Here we returning the sum of an accumulator and current value. Because of acc is always result of previous callback or initialValue we would start at 0 and add the value of each item.

transactions.reduce((acc, t) => {

return acc + t.value

}, 0)

Remove duplicated values in an array:

in this example, acc is a brand new array. We’re just checking if the current item is already included in the array and if not we will add it there. Worth to mention, that at the end we always return acc because as you remember acc value always a result of previous callback or initialValue

const numbers = [0, 0, 1, 1, 2, 3] numbers.reduce((acc, n) => {

if(!acc.includes(n) {

acc.push(n)

} return n

}, [])

reduce as a performance optimization:

The ability to combine methods is absolutely stunning. But sometimes it might be not the most optimized way to do stuff.

We gonna use the same example as in “Combining methods” section. Imagine the case where we have a list of 100 passengers. And we want to first filter full-grown people and then show their full names. Imagine if all people are full-grown in that list. It would mean that we would iterate 100 times with filter and 100 times with map or 2n times. Seem not really good, don’t you think? The same task is done with reduce would run only n times.

passenger

.reduce((acc, p) => {

if(p.age > 18) {

acc.push(`${p.name} ${p.surname}`)

} return acc

}, [])

But unfortunately with luck of readability. So in case if you’re working with small arrays I would still recommend use combination of methods approach otherwise reduce is always there to help you.

Conclusion

As you can see array has some pretty good and handy method for data manipulation. We started with how to iterate through the array, and then switched to how to filter and modify an array. How to combine these approaches together. And how to do some different thing with reduce . But there is more, which I’ll try to cover in the next topic, but for now, I’m encouraging you to go and play with arrays.

Used resources: