Powerful array filtering with Javascript's some() and every()

26 August, 2021
7 min read

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

  • basic understanding of for loop in Javascript
  • basic understanding of Array.prototype.filter()
  • a quick skim over the concept of Truthy and Falsy values in Javascript.

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:

  1. takes an array and a test function as parameters
  2. loop through the array
  3. pass an element of the array in each loop to the test function
  4. if the test function returns true, break out of the loop and return true
  5. 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:

  1. Write our test function:
function isEven(num) {
  return num % 2 === 0
}
  1. 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 return true 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".