The filter method in Javascript creates a shallow copy of an array, filtering it based on a number of conditions. It accepts a callback function. The array which filter
produces will usually be a reduced version of the original array. Here is a basic example:
let myArray = [ '⚡️', '🔎', '🔑', '🔩' ];
let filteredArray = myArray.filter((element) => {
return (element == '🔑' || element == '🔩'))
});
console.log(filteredArray); // [ '🔑', '🔩' ]
As you can see, the filter method will allow an element to be in the new, filtered array, should that element return true
. It effectively loops through each element, and runs a test on it, to see what should be left. Since arrow functions return true implicitly on a single line, you might see a more reduced version of this code written like this:
let myArray = [ '⚡️', '🔎', '🔑', '🔩' ];
let filteredArray = myArray.filter(element => element == '🔑' || element == '🔩');
console.log(filteredArray); // [ '🔑', '🔩' ]
The filter method callback function
As mentioned, filter
accepts a callback function. The callback function consists of 3 arguments:
Array.filter((element, index, array) => {
// Filter the array
});
Let's look at what each of these do
element
This is the current element being examined by filter
. filter
goes through each item in the array to test if it should exist in the new, filtered array.
index
This is the zero based index of the array item we are working with. For example, if we were viewing the first element in the array, this would be 0
.
array
This is the entire array, should you wish to do something with the original array.
Mutating arrays within the filter method
Since filter uses a callback function, it is possible to mutate the original array we are examining. For example, we could push new items to the array each time we filter over an item:
let myArray = [ '⚡️', '🔎', '🔑', '🔩' ];
let filteredArray = myArray.filter((element) => {
myArray.push('⚡️');
return true;
});
console.log(filteredArray); // [ '⚡️', '🔎', '🔑', '🔩' ]
As you might have already realised, this would produce an infinite loop. Fortunately, much like in reduce, Javascript does not allow this to happen - instead, any new elements to an array added in this way are ignored. However, mutation of existing elements is totally fine:
let myArray = [ '⚡️', '🔎', '🔑', '🔩' ];
let filteredArray = myArray.filter((element, index) => {
myArray[index + 1] = '⚡️';
return (element == '⚡️');
});
console.log(filteredArray); // [ '⚡️', '⚡️', '⚡️', '⚡️' ]
Filtering Arrays of Objects
Filtering arrays of objects follows the same conventions as with arrays. We can filter upon child properties of objects within an array by using the .
notation. For example, if I wanted to filter the following array by age
, where age should be >18, I would do something like this:
let myArray = [ { age: 4 }, { age: 12 }, { age: 19 }, { age: 21 } ];
let filteredArray = myArray.filter((element, index) => {
return (element.age > 18);
});
console.log(filteredArray); // [ { age: 19 }, { age: 21 } ]
Filtering Arrays by Search Criteria
A common use for filter
is to take an array of values, and search through them based on a search term. This can be done with includes
, or regex
. For example:
let myArray = [ 'cat', 'catdog', 'dog', 'fish', 'fishcat' ]
let filteredArray = myArray.filter((element, index) => {
return element.match(/cat/)
});
console.log(filteredArray); // ['cat', 'catdog', 'fishcat']
Filter makes a Shallow Copy of Arrays
Although it may appear that filter
makes a new copy of the original array, this is not the case. In fact, filter
makes a shallow copy of the original array, which means that if we mutate objects within the array, the original will change too. To understand this, let's look at our age example again:
let myArray = [ { age: 4 }, { age: 12 }, { age: 19 }, { age: 21 } ];
let filteredArray = myArray.filter((element, index) => {
return (element.age > 18);
});
console.log(filteredArray); // [ { age: 19 }, { age: 21 } ]
filteredArray[0].age = 50;
filteredArray[1] = { age: 40 };
console.log(filteredArray); // [ { age: 50 }, { age: 40 } ]
console.log(myArray); // [ { age: 4 }, { age: 12 }, { age: 50 }, { age: 21 } ]
As you can see, modifying the filtered array using the filteredArray[0].age
notation also modifies the original - but wait! filteredArray[1] = { age: 40 }
changes only the filtered array. That's because although Javascript interprets the .
notation as updating both arrays, it interprets the square bracket notation []
as setting a new value at the second position of the filtered array.
This is just another quirk of Javascript which can be confusing, but is very useful to know!
Conclusion
The filter method is widely used and an easy way to change create new subsets of arrays based on certain criteria. It should be noted that only a shallow copy is made of the original array, so modifying the array in certain ways will affect the original.