How to Use JavaScript some method

Necati Özmen - Oct 25 '22 - - Dev Community

Author: Abdullah Numan

Introduction

This post is about the some() method in JavaScript. This is the second part of the series titled Handy JavaScript Iteration Methods.

In this post, we explore with examples what the JavaScript some is, how it works - with and without the thisArg and see the impact of modifying the caller array from inside.

We'll discuss things in quite depth, so let's start with the basics.

Steps we'll cover:

What is JavaScript some method?

Array.prototype.some() is a JavaScript iteration method that checks whether any one element in an array satisfies a given condition. The method is called on an array of items and the condition is checked with a callback function and any necessary thisArg object passed to the execution context of the callback function:

// Method signature

some(callbackFn)
some(callbackFn, thisArg)
Enter fullscreen mode Exit fullscreen mode

The first argument, callbackFn, is mandatory and the second argument, thisArg, is optional.

callbackFn, in turn, takes three arguments. The first is the element being traversed to, element, which is mandatory. The second argument is the current index, index and the third is array, the array being iterated. Both the second and third arguments are optional:

// Method signature

some(function(element){...});
some(function(element, index){...});
some(function(element, index, array){...});
Enter fullscreen mode Exit fullscreen mode

How Array.prototype.some() Works

JavaScript some tests whether there is one element that satisfies the condition set in the callback function, callbackFn. It attempts to execute the callback function once for each item in the array. If it finds one that evaluates to a truthy value for callbackFn, it returns with the Boolean true. Otherwise, it seeks to traverse to the end of the array returns false if all are falsy:

const numbers = [1, 2, 3, 4, 5];

const even = element => element % 2 === 0;
const isThereEvenNumber = numbers.some(even);

console.log(isThereEvenNumber); // true
Enter fullscreen mode Exit fullscreen mode

In the chunk of code above, even() is our callback function, which we pass in to some(). Apparently, we have at least one even number in our numbers array. So, some() returns true.

JavaScript some() With thisArg Argument

We can pass in the thisArg object to JavaScript some() to add it to the execution context of the callback function. Let's try doing that by making some modifications to our callback.

Instead of checking for an even number, let's say we want to generalize our callback function to check if the item is divisible by a given number. We would like our callback to be something like the below:

function divisible(element, divisor) {
  return element % divisor === 0;
};
Enter fullscreen mode Exit fullscreen mode

However, we cannot pass divisor as the second argument to divisible(), as our callback accepts index and array as the second and third arguments, respectively. And it becomes overcrowded if we introduce a fourth with divisor.

We can get around this problem by passing divisor as a property of the thisArg object, the second argument to every(). And then access the object with this from inside the callback:

const numbers = [1, 2, 3, 4, 5];

function divisible(element) {
  return element % this?.divisor === 0;
};

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });

console.log(isThereEvenNumber); // true
Enter fullscreen mode Exit fullscreen mode

Here, we set the thisArg object to { divisor: 2 }, which basically leads to checking if the item is even or not.

We can try other divisor options, like checking if we have a number divisible by 3 or 7. Thanks to thisArg, this has become very easy now:

const isThereAnyDivisibleByThree = numbers.some(divisible, { divisor: 3 });
const isThereAnyDivisibleBySeven = numbers.some(divisible, { divisor: 7 });

console.log(isThereAnyDivisibleByThree); // true
console.log(isThereAnyDivisibleBySeven); // false
Enter fullscreen mode Exit fullscreen mode

Backend devs love this React framework!

Meet the headless, React-based solution to build sleek CRUD applications. With refine, you can be confident that your codebase will always stay clean and boilerplate-free.

Try refine to rapidly build your next CRUD project, whether it's an admin panel, dashboard, internal tool or storefront.


refine blog logo


some(callback, thisArg) Doesn't Work With Arrow Functions

If we look back at the first example that involves the even() callback, we define it as an arrow function. And it worked.

We defined its extension, the divisible() function with named declaration syntax. And it worked as well.

If we declare divisible() as an arrow function, we run into problems:

const divisible = element => element % this?.divisor === 0;

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });
const isThereAnyDivisibleByThree = numbers.some(divisible, { divisor: 3 });
const isThereAnyDivisibleBySeven = numbers.some(divisible, { divisor: 7 });

console.log(isThereEvenNumber); // false
console.log(isThereAnyDivisibleByThree); // false
console.log(isThereAnyDivisibleBySeven); // false
Enter fullscreen mode Exit fullscreen mode

All returning false, although we expect two to be true and one to be false.

If we investigate the problem with a modified divisible() function that logs this to the console, we see that this is undefined in strict mode:

// strict mode

const numbers = [1, 2, 3, 4, 5];

const divisible = element => {
  console.log(this);
  return element % this?.divisor === 0;
};

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });

console.log(isThereEvenNumber);
// undefined
// undefined
// undefined
// undefined
// undefined
// false
Enter fullscreen mode Exit fullscreen mode

Now, if we introduce a this.divisor property to the lexical environment of divisible(), we get its value logged to the console:

const numbers = [1, 2, 3, 4, 5];
this.divisor = 'Hi';

const divisible = element => {
  console.log(this);
  return element % this.divisor === 0;
};

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });

console.log(isThereEvenNumber);
// { divisor: 'Hi' }
// { divisor: 'Hi' }
// { divisor: 'Hi' }
// { divisor: 'Hi' }
// { divisor: 'Hi' }
// false
Enter fullscreen mode Exit fullscreen mode

Here, clearly, we have { divisor: 'Hi' } coming from divisible()'s closure. It turns out, the problem is due to the binding of divisible()'s this to it's lexical environment because of the arrow syntax. It was undefined before we introduced this.divisor = 'Hi';. Now this is { divisor: 'Hi' }. In other words, { divisor: 2} is not being relayed to divisible's this.

So, some() with thisArg does not work as expected with callbackFn defined with arrow syntax.

some(callback, thisArg) Works With Non-Arrow Functions

But as we have seen before, it works with callbacks defined with named function declarations:

function divisible(element) {
  return element % this?.divisor === 0;
};

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });

console.log(isThereEvenNumber); // true
Enter fullscreen mode Exit fullscreen mode

It also works with anonymous function expressions:

const divisible = function(element) {
  return element % this?.divisor === 0;
};

const isThereEvenNumber = numbers.some(divisible, { divisor: 2 });

console.log(isThereEvenNumber); // true
Enter fullscreen mode Exit fullscreen mode


discord banner

Modifying the Caller Array

JavaScript some method sets the range of the items to be processed before the first invocation of the callback function.

some() itself does not modify the caller array, but the caller is available to the callback function as its third argument, array. And if an item is changed after traversal, the change is disregarded by the callback function. That is, the callback function only respects the existing value of an item at the time it is visited.

We can witness this in a scenario where the caller array is mutated from inside some().

JavaScript some() itself does not modify the caller array, but the caller is available to the callback function as its third argument, array. This means we can deliberately mutate the caller when we need to from inside our callback, divisible():

function divisible(element, index, array) {
  array[0] = 7;
  array[4] = 7;
  console.log(array);
  return element % this?.divisor === 0;
};
Enter fullscreen mode Exit fullscreen mode

In this scenario, if an unvisited item is changed ahead of time, the callback function - here divisible() - finds the new value when it visits the item and so the new value is processed. In contrast, it disregards changes to items that are already traversed:

const divisible = function(element, index, array) {
  array[0] = 7;
  array[4] = 7;

  console.log(array);

  return element % this?.divisor === 0;
};

const isDivisibleBySeven = numbers.some(divisible, { divisor: 7 });

console.log(isDivisibleBySeven);
console.log(numbers);
/*
  [ 7, 2, 3, 4, 7 ]
  [ 7, 2, 3, 4, 7 ]
  [ 7, 2, 3, 4, 7 ]
  [ 7, 2, 3, 4, 7 ]
  [ 7, 2, 3, 4, 7 ]
  true
  [ 7, 2, 3, 4, 7 ]
*/
Enter fullscreen mode Exit fullscreen mode

In the console log statements above, the numbers array is being logged five times due to the console.log(array); statement we placed inside divisible().

As we can see, numbers is being mutated twice in the first call to divisible(). The first mutation happens when at numbers[0], i.e. after being visited, which changes the value of itself to 7. So, even though it was divisible by the divisor 7, some() didn't immediately return true. Instead, it returned true in the next instance when it visited the unvisited and mutated value of 7 at numbers[4].

This shows that the callback function processes the value of an item as it finds it at traversal and disregards the changes made to it when and after it is at that index.

Conclusion

In this article, we focused on JavaScript some method which helps us test whether an array has at least one item that passes the test we implement using a callback function. We saw that the callback function could take only three arguments, and additional arguments can be bound to its execution context by setting its this value with a thisArg passed to some().

We also found out that if we use arrow syntax to declare the callback function, its lexical context binding messes with the binding of thisArg to its this object. So, we should be using non-arrow functions to declare a callback function that uses this.

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .