Functional Basics #2: Filter

kvsm

Kevin Smith 🏴󠁧󠁢󠁳󠁣󠁴󠁿

Posted on October 20, 2019

Functional Basics #2: Filter

Welcome back! In the second part of this mini-series on functional programming basics, I'll be taking a quick look at another common array function - filter.

If you haven't already read it, I recommend starting with the first article in this series, Functional Basics #1: Map. I'll be building on some ideas as the series progresses.

What is it?

Let's again start by defining what filter does: it takes an array and removes some elements, leaving only the elements that we're interested in. Okay, but how does it do that? We'll first take a look at how we'd do it in the imperative style, using for loops:

const myArray = [1, 2, 3, 4, 5]
let oddNumbers = []

for (let i = 0; i < myArray.length; i++) {
  if (myArray[i] % 2 === 1) {
    oddNumbers.push(myArray[i])
  }
}

// myArray: [1, 2, 3, 4, 5]
// oddNumbers: [1, 3, 5]

As we did with map in Part 1, we can tidy this up by extracting some of the logic from our loop - specifically, we can extract the condition of the if statement to make it clear what that condition actually means:

const isOdd = num => num % 2 === 1

const myArray = [1, 2, 3, 4, 5]
let oddNumbers = []

for (let i = 0; i < myArray.length; i++) {
  if (isOdd(myArray[i])) {
    oddNumbers.push(myArray[i])
  }
}

// isOdd(1): true
// isOdd(2): false
// myArray: [1, 2, 3, 4, 5]
// oddNumbers: [1, 3, 5]

Having extracted the logic to an isOdd function, you can now see clearly that we're checking for odd numbers in our if statement.

A function like isOdd which determines whether a value meets a certain criteria is known as a predicate function.

But we still have a for loop which is a potential opportunity to introduce bugs, and we still have to mutate our oddNumbers array by adding results to it one by one.

If we try to describe what we want in a declarative way, we could say, "take myArray and produce a new array which only contains the elements for which isOdd is true". And filter allows us to express exactly that:

const isOdd = num => num % 2 === 1

const myArray = [1, 2, 3, 4, 5]

const oddNumbers = myArray.filter(isOdd)

// myArray: [1, 2, 3, 4, 5]
// oddNumbers: [1, 3, 5]

Calling filter on an array, and passing it a predicate function, returns a new array containing the elements for which the predicate returns true.
Also note the original array is not mutated by filter - as with map, the result must be assigned to a variable or used immediately!

Also, as with map, we're not restricted to using our own functions as the predicate. We can use any function which takes one value and returns true or false. Javascript has a global function isNaN which returns true if the value passed to it is not a number, and false if it is. Let's use that to filter out numbers from an array:

const myArray = [1, 2, 'apple', 3, 'orange', 4, 'banana']

const arrayWithoutNumbers = myArray.filter(isNaN)

// myArray: [1, 2, 'apple', 3, 'orange', 4, 'banana']
// arrayWithoutNumbers: ['apple', 'orange', 'banana']

And that pretty much sums it up for filter. Again, aside from looking specifically at filter, it's important to reinforce the idea that functions are just values which can be passed to and returned from other functions, like any other value. Stay tuned for Part 3!

If you found this article helpful, follow me! I'll be adding more articles in this series soon. Liked it? Like it ❤️! Suggestions/improvements? Comment ⬇️! :)

💖 💪 🙅 🚩
kvsm
Kevin Smith 🏴󠁧󠁢󠁳󠁣󠁴󠁿

Posted on October 20, 2019

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

Sign up to receive the latest update from our blog.

Related