Blog

Front End Development topics with a touch of humor

Filter

Keep the good, leave the rest

September 30, 2020


Say you have an array of elements and want to pare them down. How would you do that?

You could use our friend, the Swiss Army knife for loop, or its cousin the while loop, with a control statement. In this case, we have an array of numbers from 1 to 5, and we only want the ones less than 4.

            let arr = [1, 2, 3, 4, 5];
            let newArr = [];
    
            for (let i = 0; i < 5; i++) {
                if (arr[i] < 4) {
                    newArr.push(arr[i]);
                }
            }
            
            console.log(newArr);
            // 1, 2, 3
        

or

            let arr = [1, 2, 3, 4, 5];
            let newArr = [];
            let i = 0;
            while (arr[i] < 4) {
                newArr.push(arr[i]);
                i++;
            }

            console.log(newArr);
            // 1, 2, 3
        

Enter filter()

But given the title of this post, you probably know we're not going to end there. Let's switch it up to filter and compare the results.

            let arr = [1, 2, 3, 4, 5];
            let newArr = arr.filter((item) => item < 4);
   
            console.log(newArr);
            // 1, 2, 3
        

It's much terser: one line of code versus four for the for loop and 5(!) for the while loop. Another benefit is it can be deployed as an expression, rather than a statement. This is helpful in React, which, I've found, favors very terse bits of code and doesn't allow statements in places where a for loop would require them.

Let's try it with strings. Say we wanted to write a program to see what the best dog was.

            let dogs = ["Beagle", "Collie", "Chihuahua", "German Shepherd", "Lab"];
            let bestDog = dogs.filter((dog) => dog === "German Shepherd");
   
            console.log(bestDog);
            // "German Shepherd"
        

Glad we got that resolved! It's quick and tidy. Compare if we were to use a for loop:

        let dogs = ["Beagle", "Collie", "Chihuahua", "German Shepherd", "Lab"];
         let bestDog;

         for (let i = 0; i < dogs.length; i++) {
             if (dogs[i] === "German Shepherd") {
                 bestDog = dogs[i];
             }
         }
         console.log(bestDog);
        

This completely scientific process had the same result both times. The debate is over!

Callbacks are where it's at

In case your ears pricked up at the shenanigans of the last section, I'll admit it: the code was rigged. Not because of anything wrong with the operations of the functions, but because of the callback function I used (this is not to say German shepherds aren't the best dogs. Clearly they are), but because the callback function was rigged.

The callback function is the first argument to filter(). They can be a little tricky to get used to, because it requires 2D thinking. The filter() has its rules, and then the callback function has its own rules.

The rules of filter() are that it tests each element in turn. If the included callback function returns true, than the element is returned by the filter (into the array the filter is set equal to). If it does not, then the element is left out.

            filter(callback function returns true) => keep the element
            filter(callback function returns false) => element goes. "filter it out"
        

So the trick of the dog filter I set up earlier is that my callback function, (dog) => dog === "German Shepherd"), will only return the item that happens to be equal to "German Shepherd". I know, I know, I cheated! But one of the issues with these callback function sis they tend to be written very tersely, so let's take one last look at this callback function to clear up any misunderstandings.

            (dog) => dog === "German Shepherd" 

            is equal to 

            function(dog) {
                if (dog === "German Shepherd") {
                    return true;
                }
            }
        

Let's play with the callback function to make the test more fair, shall we? I mean, if the callback function explicitly tests for "German Shepherd," of course it's going to be the best. Even my biased self can tell that's not completely fair.

            let dogs = ["Beagle", "Collie", "Chihuahua", "German Shepherd", "Lab"];
         
            let bestDog = dogs.filter((dog) => dog.length > 10);
   
            console.log(bestDog);
            // "German Shepherd"
        

Look at that! A new and more impartial test. This time the callback function simply tested to see if the string was over 10 characters long. It turns out that German Shepherd is still considered the best dog.

What filter() is not

Notice that filter returns an element if the callback function returns true. It doesn't change the element during the process. For that, you can use forEach() or map(), two other cool iterative methods. You might say that filter() is subtractive... it only takes, doesn't change or add.

Now--there's a lot more you can do with filter. If you need the index, of the original array, that is the second argument of the callback function. if you need to refer to the original array, that is the third parameter.

        let dogs = ["Beagle", "Collie", "Chihuahua", "German Shepherd", "Lab"];
         
         let bestDog = dogs.filter((dog, index) => (index > 2 && index < 4));

         console.log(bestDog);
         // "German Shepherd"
        

Notice that this callback function uses the second parameter, called index, for its test. Again, it reveals the truth in a most impartial manner.

Image by skeeze from Pixabay