JS: Filter, Map, and forEach explained.
Brent Dalling
Posted on November 4, 2022
We've all been there as developers. We've got an array of data that we need to loop through. As a junior developer you might not know which built-in JS function to use. So you go to Stackoverflow. You see solutions to your problem using .filter()
, .map()
, and .forEach()
. But what are these? And, when should you really use them? Let's find out.
.filter
.filter
does excactly what it sounds like. It loops through the iterable object or array and returns a new value. For this reason you should never use this function for just the looping side-effect. As using it for this reason causes more memory to be assigned in your application.
Let's review the proper usage of this built-in javascript function and compare it against the side-effect usage.
// Proper Usage
const fruits = ['apple', 'orange', 'kiwi']
const fruitsMinusKiwi = fruits.filter(fruit => {
return fruit !== 'kiwi'
})
// Improper Usage (Side-effect)
const fruits = ['apple', 'orange', 'kiwi']
fruits.filter(fruit => {
console.log(fruit)
return fruit
})
At first look they seem similar. They even seem to use the same amount of memory and time to complete. One simply filters the array and one simply logs the contents. The usage that logs the contents doesn't actually assign any values. So this is okay right? Well, no. Not really. This function does go through the extra step of attempting to return the values.
This difference is honestly negligible in most apps. In fact, you probably don't even need to worry about it. However, if you are ever working on large scale applications that require highly available and performant resources for thousands of active end-users then this absolutely matters.
See, as the dataset we are looping though increases. So does our completion time. For example, if this loop takes 2ms for 10 records to loop through using the proper usage and 3ms for the improper usage. Then you have 2ms and 3ms respectively. However, let's add 10,000 records to loop through. Now we have 2 seconds and 3 seconds respectively. The improper usage now takes a full second longer to complete its code.
// Resources & Links
https://leanylabs.com/blog/js-forEach-map-reduce-vs-for-for_of/
.map
.map
is very similar to the .filter
function. However, this built-in function does't try to return a new filtered array. Instead, this function returns a new array with all returned data. Technically you can still filter by just not returning anything for the current iteration.
// Proper Usage
const fruits = ['apple', 'orange', 'kiwi']
const fruitsLowerCased = fruits.map(fruit => {
return fruit.toLowerCase()
})
// Improper Usage (Side-effect)
const fruits = ['apple', 'orange', 'kiwi']
fruits.map(fruit => {
console.log(fruit)
})
---
// Improper Usage (pushing to array rather than assign)
const fruits = ['apple', 'orange', 'kiwi']
let fruitsLowerCased = []
fruits.map(fruit => {
fruitsLowerCased.push(fruit.toLowerCase())
})
The above incorrect examples show us either using .map
as a side-effect to simply log a fruit or as a loop to push to a different array fruit values in lowercase form. Both of these use more memory than is required. In small projects this is likely fine. However, just like the .filter
use-case, doing this incorrectly will result in larger computational time requirements in large data-set environments.
.forEach
This built-in function doesn't actually return anything. Instead, it is a literal for loop that just iterates through an iterable and provides each value in a callback. For example:
const fruits = ['apple', 'orange', 'kiwi']
fruits.forEach(fruit => console.log(fruit))
This is very similar to:
const fruits = ['apple', 'orange', 'kiwi']
for (let fruit of fruits) {
console.log(fruit)
}
Both are correct ways of iterating through an object without generating more data. Rather, you are taking already available data in the memory space and are reading or manipulating it. For example, let's lowercase our values.
- Note, since these don't return anything (void functions) we need to have a variable outside of the loop scope to record our lowercase values.
- Therefore these should not be used in an instance where you need data out. Rather use a
.filter
or.map
function.
const fruits = ['apple', 'orange', 'kiwi']
fruits.forEach(fruit => console.log(fruit.toLowerCase))
---
const fruits = ['apple', 'orange', 'kiwi']
for (let fruit of fruits) {
console.log(fruit.toLowerCase())
}
Not
const fruits = ['apple', 'orange', 'kiwi']
let fruitsLowerCased = []
for (let fruit of fruits) {
fruitsLowerCased.push(fruit.toLowerCase())
}
---
const fruits = ['apple', 'orange', 'kiwi']
let fruitsLowerCased = []
fruits.forEach(fruit => fruitsLowerCased.push(fruit.toLowerCase))
Okay. So we have an understanding of how these work and how to use them. Let's look at a few real world examples. Checkout the example below!
--
If you enjoyed this article please drop a comment and share what you learned! Did I miss something? If so, please let me know!
Posted on November 4, 2022
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.