JS: Filter, Map, and forEach explained.

brentdalling

Brent Dalling

Posted on November 4, 2022

JS: Filter, Map, and forEach explained.

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'
})
Enter fullscreen mode Exit fullscreen mode
// Improper Usage (Side-effect)
const fruits = ['apple', 'orange', 'kiwi']

fruits.filter(fruit => {
    console.log(fruit)
    return fruit
})
Enter fullscreen mode Exit fullscreen mode

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()
})
Enter fullscreen mode Exit fullscreen mode
// 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())
})

Enter fullscreen mode Exit fullscreen mode

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))
Enter fullscreen mode Exit fullscreen mode

This is very similar to:


const fruits = ['apple', 'orange', 'kiwi']

for (let fruit of fruits) {
    console.log(fruit)
}
Enter fullscreen mode Exit fullscreen mode

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())
}
Enter fullscreen mode Exit fullscreen mode

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))

Enter fullscreen mode Exit fullscreen mode

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!

💖 💪 🙅 🚩
brentdalling
Brent Dalling

Posted on November 4, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related