filter()

In this tip, you’ll learn how to change the size of an array while retaining the shape of the items.

We know how to transform our array with map. But note that map just returns an array to us with the same length. What if on the other hand we just want a subset of data; a slice of the array. And we don't want to transform the array, we just want to retrieve it.

A great use of the filter function is for searching. Say for example we are making a restaurant finder app. And we have a big dataset of restaurants that people can search through, where they type in the restaurant name they're looking for.

const restaurants = [
  { name: "Cap City Diner", milesAway: 2.2 },
  { name: "Chop Shop", milesAway: 4.1 },
  { name: "Northstar Cafe", milesAway: 0.9 },
  { name: "City Tavern", milesAway: 0.5 },
  { name: "Shake Shack", milesAway: 5.3 },
];

So we have this list of restaurants and we want to test out our search. How will can we just get those restaurants that begin a certain way, say with the letter C. We see that there are a number of them.

We can use filter just like any of our other array methods. And like some and every, it accepts a function passed to it, where we can supply our condtion:

restaurants.filter();

So in this case, for each restaurant, we want only those whose name starts with the letter C. Fortunately there's a new string method that does just that, it tells us whether the string startsWith the text we provide to it.

So since it iterates over each element, we'll call each one restaurant. And then in the return create our condition, where we're looking to see whether the restaurant name from restaurant.name startsWith the capital letter. We'll put the results in a variable of the same name:

const results = restaurants.filter((restaurant) =>
  restaurant.name.startsWith("C")
);
console.log(results);

So great, we get a new array of three elements whose names all begin with C. And just like map, it returns to us a new array and doesn't mutate the original. We can console.log restaurants as well to see this:

// console.log(results)
console.log(restaurants);

Now let's try a condition that we know won't work, such as all of the restaurants that begin with Z. What do you think will happen:

const results = restaurants.filter((restaurant) =>
  restaurant.name.startsWith("Z")
);
console.log(results); // []

We just get an empty array. So be aware of that behavior. If filter finds a number of elements, it collects them all and returns them in a new array, otherwise, if none of the elements match the condition, it returns an empty array.

And we've covered this for .some() and .every(), but note that the behavior is the same for .filter(), that filter just gives us the array elements for which the condition was met, in other words, where the return value is true. We can see this on display if we set the return value of filter's function to true.

So what do you think will happen when we run this?

const results = restaurants.filter((restaurant) => true);
console.log(results);

We get all of the array elements returned to us. None of them are filtered out. So be aware of that--filter works by checking if the return value of the condition is true for every element. If it is, it's added to the new filtered array, and if not, it's not included.

Let's look at more advanced filtering. Let's say that we want to have multiple conditions. We want the restaurants that start with C that are less than three miles away. If you can, take a minute and see if you can figure out a solution yourself, otherwise we'll solve it together...

So first of all, we can use the condition we had before and if we want to continue using an arrow function, we can use what we know about shortcircuiting to help us. So if it startsWith C and (using the double and operator) the milesAway property is less than three, it's a match.

const results = restaurants.filter(
  (restaurant) => restaurant.name.startsWith("C") && restaurant.milesAway < 3
);
console.log(results);

And we get two matches: Cap City and City Tavern that meet both of those conditions.

.find()

Filter is a very helpful array method for searching and returning subsets of arrays, but what if we know that we just want one element. Say we're looking for a single restaurant in particular that has north in the name and is less than 3 miles away.

If we just need one and only one element from an array, not a subset of one, we can use the find method. It works just like filter, but it finds just the element we're looking for or nothing:

So let's replace filter with find, but in this case, to find north in the name somewhere, we can use the string method includes (not the array method). However, includes is case sensitive. So if we use north lowercase, we won't get a match if it is capitalized. So what we can do it chain on toLowercase(). This will convert each of the restaurant names to lowerCase. And what's cool is that we can use chaining for string methods just like array methods.

So after that, we'll pass in north and remember our other condition is that it is less than 3 miles away, so we'll use the && operator again. Finally, we'll put the result in a variable called result.

const result = restaurants.find(
  (restaurant) =>
    restaurant.name.toLowerCase().includes("north") && restaurant.milesAway < 3
);
console.log(result); // {name: "Northstar Cafe", milesAway: 0.9}

And we get what we were looking for Northstar Cafe. And note that if we don't get a match with find, we just get undefined.

So in review, if you're looking for the best way to get a selection of an array and also to search arrays based on multiple conditions, use the filter method. And if you know in advance that you just need one array element, make use of find. They both operate in the same way. ​ ​