Powerful array filtering with Javascript's some() and every()
WHAT YOU WILL LEARN
In this article we will explore the some()
and every()
methods by first
writing them from scratch, and then combining them filter()
to search for
blog posts by keywords and/or tags.
PREREQUISITES
Basics
Javascript comes packed with many built-in array manipulation methods. Two of
my favourites, particularly when used in combination with filter()
, are the
some()
and every()
methods.
The some and every methods exist on the array object, for example
Array.prototype.some()
.
By understanding how these methods are implemented, you will gain an appreciation for Javascript's native array methods to write cleaner, earier to read code.
some()
The some()
method takes a test function as a parameter and returns true
if
any element in an array passed into the test function results in a Truthy
return value.
some()
could be thought of as an OR
operator:
pass if a OR b OR c is true.
Before we go ahead and use the some()
method, let's go ahead and implement it
ourselves without using any other array methods.
To implement a basic version of the some()
method, we must write a function that:
- takes an array and a test function as parameters
- loop through the array
- pass an element of the array in each loop to the test function
- if the test function returns true, break out of the loop and return
true
- otherwise if no elements of the array pass the test function, return
false
For example if we wanted to test that any number in an array was an even number, we could implement it as follows:
- Write our test function:
function isEven(num) {
return num % 2 === 0
}
- Implement our own version of
some()
:
function some(array, test) {
// loop through each element of the array
for(let i = 0; i < array.length; i ++) {
// if the element passes the test function
if(test(array[i])) {
// exit out of the function, returning true
return true;
}
}
// if no tests above pass, then return false
return false
}
const array1 = [1,3,6,7]
const array2 = [1,3,5,7]
some(array1, isEven) // -> true since 6 passes
some(array2, isEven) // -> false since all fail
And now using the native some()
method:
array1.some(isEven) // -> true since 6 passes
array2.some(isEven) // -> false since all fail
every()
The every()
method takes a test function as a parameter and returns true
if
all elements in the array result in a Truthy return value from the test
function. every()
returns false
if any element of the array results in a
Falsy return value.
every()
could be thought of as an AND
operator:
pass if a AND b AND c is true.
Let's start by implementing our own every()
function from scratch, using the same
isEven()
function we created previously:
function every(array, testFn) {
// loop through every element
for(let i = 0; i < array.length; i ++) {
// as soon as an element fails the test
if (!test(array[i])) {
// return false, breaking out of the function
return false
}
// otherwise return true
return true
}
}
const array1 = [2,4,6,7]
const array2 = [2,4,6,128]
every(array1, isEven) // -> false since 7 fails
every(array2, isEven) // -> true since all pass
Now we can implement the above using the native every()
method:
array1.every(isEven) // -> false since 7 fails
array2.every(isEven) // -> true since all pass
Real world use case
The SearchBox
component on this website relies on several filters. One
filter searches the blog titles for the entered searchstring, and the other
searches blog posts for a combination of selected tags (I still have some work
to do to search blog post content).
Searching blog post content with string search using some()
One of the next features I want to implement is searching blog post content by
strings entered into the SearchBox
. To achieve this, we will do the
following. To check if a 'sentence' contains a 'word', we could use some()
, but
first we would need to convert the string into an array of words, since the some()
method exists in Javascript's Array
object, and not on String
.
Let's start with something simple first - let's test if a sentence contains a word:
const word = "funny"
const sentence1 = "This sentence is serious"
const sentence2 = "This sentence is funny"
// convert string into an array of strings, separated at each space
const sentence1AsArray = sentence1.split(" ")
const sentence2AsArray = sentence2.split(" ")
// check if `word` exists in each array, using `contains()`
sentence1AsArray.contains(word) // -> false
sentence2AsArray.contains(word) // -> true
Now let's test if a sentence contains multiple words:
const words = "peas carrots"
const sentence1 = "I like carrots"
const sentence2 = "I like peas and carrots"
const sentence3 = "I like water"
// convert string into an array of strings, separated at each space
const sentence1AsArray = sentence1.split(" ")
const sentence2AsArray = sentence2.split(" ")
const sentence3AsArray = sentence3.split(" ")
// check if all words exist in a sentence
words.some(word => sentence1AsArray.contains(word)) // true
words.some(word => sentence2AsArray.contains(word)) // true
words.some(word => sentence3AsArray.contains(word)) // false
// check if all words exist in a sentence
words.every(word => sentence1AsArray.contains(word)) // false
words.every(word => sentence2AsArray.contains(word)) // true
words.every(word => sentence3AsArray.contains(word)) // false
Searching by multiple tag selection using every()
The filter function powering the SearchBox
component used on this blog
implements the every()
method to filter blog posts by tag. When a user clicks
on a tag below the search box, only blog posts containing that tag are
rendered. If more than one tag is selected, then only blog posts containg
every tag is shown. This is testing if one array is a subset of another.
Below is a simplified version of what is going on, checking if an array is a subset of another:
const blogPosts = [
{
id: 1,
title: 'array methods',
tags: ['javascript', 'arrays']
},
{
id: 2,
title: 'ASCII spaces',
tags: ['misc']
},
{
id:3,
title: 'How to debug NodeJS in Vim',
tags: ['javascript']
},
]
const selectedTags = ['javascript', 'arrays']
function isSubset(array1, array2) {
// for every element in the array
return array2.every(
// return true if the element exists in array1
element => array1.includes(element)
)
}
const filteredBlogPosts =
blogPosts.filter( post => isSubset(post.tags, selectedTags))
// only one post contains both selected tags:
// -> [ { id:1, title: 'array methods', tags: [ 'javascript', 'arrays' ] } ]
Note that the only returned blog post is post id: 1
since that is the
only post containing both tags in selectedTags
.
I don't know about you, but due to naming, this was a bit confusing for me at first!
GOTCHYAS
every()
will returntrue
if passed an empty array.- beware of some quirks with Javascript's Truthy values, particularly empty
arrays
[]
, empty objects{}
, the string"0"
and the string"false"
.