By Vincent Driessen
on Monday, December 06, 2021

We can all pretty intuitively understand how .some() and .every() predicate expressions on lists work.

Let's define a list of Simpsons:

const simpsons = [
  { name: 'Homer', age: 39 },
  { name: 'Marge', age: 37 },
  { name: 'Bart', age: 13 },
  { name: 'Lisa', age: 11 },
  { name: 'Maggie', age: 4 },
];

And a predicate to tell if someone is an adult:

function isAdult(simpson) {
  return simpson.age >= 18;
}

Then, these statements are pretty obvious:

simpsons.some(isAdult);   // => true, because Homer and Marge are adults
simpsons.every(isAdult);  // => false, because not everyone is an adult

But it's not immediately obvious why these are the defaults for an empty list:

[].some(isAdult);   // => false
[].every(isAdult);  // => true

Every person in the empty list is an adult? That sounds weird when you say it.

It doesn't matter what predicate you pass in here though, it will not affect the result. In other words, every person in the empty list is also NOT an adult 😉

One intuition that can help with this, is to think of .some() and .every() as chains of logical "or" and "and" expressions:

// .some() is a chain of "or"s
isAdult(homer) || isAdult(marge) || isAdult(bart) || ...

// .every() is a chain of "and"s
isAdult(homer) && isAdult(marge) && isAdult(bart) && ...

You can tack a constant to the beginning of these chains in a way that keeps them logically equivalent:

false || isAdult(homer) || isAdult(marge) || isAdult(bart) || ...
true  && isAdult(homer) && isAdult(marge) && isAdult(bart) && ...

And that's why those are the defaults for the empty list!

Other posts on this blog

If you want to get in touch, I'm @nvie on Twitter.