every and some: Learning JavaScript's Array Methods by Building Them
This article walks through how to use and implement the every and some array methods in JavaScript.
March 24, 2021 | 7 minute readI’ve made a lot of progress on the list of array methods, having covered eight so far. In this article, I’ll cover two more: every
and some
, both of which are great methods for understanding the contents of an array.
How They Works
It’s pretty common to want to test the contents of an array before running some code against it. Perhaps you need to check that every user is active before starting the game loop in an online game. In JavaScript-speak, you want to check that the active
property is set to true
for every user
object in the array of users. This is where the every
method comes in handy: it will return true
or false
based on a test function.
The every
method checks that every object passes the test; the some
method checks that at least one object passes the test (i.e. that some of them do). So if you need at least one user to be a host for the game to start (i.e. at least one user
object in the users array needs to have a host
property of true
) you can use some
to check for that.
The every Method
Thinking back, my implementation of the concat
method took any number of arrays supplied as arguments and merged them together into a single array. There is one small issue with my implementation though: it doesn’t check that all the values passed in are in fact arrays:
function concat(...args) {
let final = []
args.forEach(arg => {
final = [ ...final, ...arg ]
})
return final
}
I can fix this quickly and easily with the every
method:
function concat(...args) {
let areArrays = args.every(arg => Array.isArray(arg))
if (!areArrays) return new Error("Every argument must be an array.")
// Rest of code ...
}
Now, if someone tries to pass in a value that isn’t an array, they’ll get an error back:
concat([1,2,3], 'a', [4, 5, 6])
// Error: 'Every argument must be an array.'
This is very simple error handling, but it will save my users from receiving an error that doesn’t make sense.
The important part that makes all this work is the arrow function I’m passing in to the every
method. That method must return a boolean value, which determines the final output. If every invocation of this function returns true
(or a truthy value), then the call to every
will return true
. If any one invocation does not return true
, then every
will return false
.
The some method
The some
method will return true if any item passes the test function. That is, if any of the test function invocations return true
, then the some
method will return true
:
const vals = ['a', 1, 5, NaN, true, ['h','e','l','l','o'], 'z', 'world']
vals.some(val => Array.isArray(val)) // true
In the above snippet of code, we have a bunch of random values in an array. On the second line, we’re testing that some (so, at least one) of the values is an array. The sixth item in vals
is an array of strings, so we get true
back.
I’ll admit here that I rarely find a use for the some
method. Instead, I tend to test the output of filter
, because if I need at least one of the values in an array to pass a test, I probably also need those specific values back. The filter
method lets me do both at once. If the output array of filter
has a length of 0
, then I know that no items passed my test function and can display that fact to the user. If any of them did pass the test, I now already have those values.
Implementing Our Own
We’ll start by implementing the every
method and then move on to implementing the some
method. Both implementations will look pretty similar, which is why we’re treating them both together in this article. There is only one small difference in the internal logic that makes them work.
Implementing every
To start off, I need a function that accepts an array and a callback. The callback will get invoked on every item in the array, which is how I’ll calculate our result:
function every(vals, cb) {
// More here later
}
Next, I need to loop through vals
and check that invoking cb
on each value returns true
, or a truthy value:
function every(vals, cb) {
for (let i = 0; i < vals.length; i++) {
let res = cb( vals[i] )
// More here later
}
}
I’ve got a standard for
loop that gets the current value of the array and passes it into the cb
callback function, capturing the result in res
.
Now, if res
is ever false
or falsey, I want to return false
. I can do that with a simple conditional:
function every(vals, cb) {
for (let i = 0; i < vals.length; i++) {
let res = cb( vals[i] )
if (!res) return false
}
}
If res
is every false (or, not true) then we return false
immediately. I want to make it so by default we return true
, so I can add that too:
function every(vals, cb) {
for (let i = 0; i < vals.length; i++) {
let res = cb( vals[i] )
if (!res) return false
}
return true
}
And we’re set:
const vals1 = [1, 2, 3, 4, 5]
const vals2 = [1, 2, '3', 4, 5]
const isNumber = val => typeof val === 'number'
every(vals1, isNumber) // true
every(vals2, isNumber) // false
My first array of values only includes numbers in it, so it returns true
when we pass it into our implementation of every
with the test function isNumber
. By contrast, the second array of values has a string in it ('3'
), so it returns false
.
Implementing some
My implementation of every
checks that every value passes the test function; my implementation of some
needs to check that any value passes the test function. The implementation will be very similar, but with a small tweak.
To start, I need a function, called some
that accepts an array and a callback function:
function some(vals, cb) {
// More here later
}
Then, we need to loop through the vals
array and pass each value to the cb
function, just like in our implementation of every
:
function some(vals, cb) {
for (let i = 0; i < vals.length; i++) {
let res = cb( vals[i] )
// More here later
}
}
This is the part where things become a little different: I’m going to reverse the order of my return values. If res
is true
(or truthy), then I’m going to return true
from some
. Otherwise, I’m going to return false
from some
:
function some(vals, cb) {
for (let i = 0; i < vals.length; i++) {
let res = cb( vals[i] )
if (res) return true
}
return false
}
I’ll use this to test if any of the values in the previous two arrays are strings:
const vals1 = [1, 2, 3, 4, 5]
const vals2 = [1, 2, '3', 4, 5]
const isString = val => typeof val === 'string'
some(vals1, isString) // false
some(vals2, isString) // true
The first array only contains numbers, so my invocation of some
returns false (no value is a string). My second invocation returns true because one of the values in vals2
is a string ('3'
).
Conclusion
If you’ve been following along with this series of articles then you’ll see, once again, that these methods are a lot simpler and easier to implement than you might initially think.
As I was planning out this article, I assumed I would need a flag value in every
and some
in order to make my implementations work. A flag
value is a variable you set at the beginning of the function definition and use to exit out of the function if it every changes.
That ended up being too complicated for what I needed, so I’ll have to explain it in a future article. But you can see how it’s easy to over-complicate what these methods do.
These are the seventh and eighth array methods I’ve written about. If you enjoyed this one, then there are two things you can do:
The first is to give me a follow on Twitter, so you won’t miss future articles in this series.
The second is to go and read some of the other articles I’ve written on array methods: