.

Tags:

scope
Iterating over an array to search for an item is a pretty common task. With JavaScript, Array.prototype.forEach is often the popular choice. In some cases however, Array.prototype.some can be a better fit for the job if there is no need to search the entire array once a condition is fulfilled.

There are at least three different ways to iterate over arrays and objects in JavaScript: for loops, array methods, listing property keys. If the iteration is being carried out to perform a search operation, it may lead to the following example:

function findEmployee(id) {
  for (var i in employees)
    if (employees[i].id === id)
      return employees[i];
}

Without any guards and error checks, the code above is rather self-explanatory. Now, since JavaScript Array has some powerful higher-order functions, one certainly can tweak the above code to look like this:

function findEmployee(id) {
    var employee;
    employees.forEach(function (e) {
        if (e.id === id) employee = e;
    });
    return employee;
}

Let us review what the specification says about Array.prototype.forEach (Section 15.4.4.18):

callbackfn should be a function that accepts three arguments. forEach calls callbackfn once for each element present in the array, in ascending order.

In our example problem, supposed that the employee’s id is unique (ideally of course this would be an associative container, see also the blog post Determining Objects in a Set, but that is a different story). The search via forEach is rather inefficient, it will continue even though there is already a hit. It is mandated by the above specification, invoking the callback once for each element present.

An improvement to the search would be to stop right away once the condition is fulfilled. Note that the condition needs not be only a single match. There are problems like searching the first 3 free spaces, locating enough suitable rooms, etc. Fortunately, there are Array methods which can do that: every and some. I have covered the use of every to implement a primality test (see the previous blog post on Prime Numbers, Factorial, and Fibonacci Series with JavaScript Array) so let us take a look at its sibling.

Section 15.4.4.17 of ECMAScript 5.1 specification reveals that for Array.prototype.some:

callbackfn should be a function that accepts three arguments and returns a value that is coercible to the Boolean value true or false. some calls callbackfn once for each element present in the array, in ascending order, until it finds one where callbackfn returns true.

This is exactly what we need! Rewriting the previous code fragment gives the following version. The iteration will not continue if there is a match.

function findEmployee(id) {
    var employee;
    employees.some(function (e) {
        if (e.id === id) {
            employee = e;
            return true;
        }
    });
    return employee;
}

Did you ever encounter a situation where some can be used instead of forEach? Tell us!

  • jefftschwartz

    Indeed! Array’s higher order methods are extremely useful. some() is especially useful because, as you pointed out, the inefficiency of [].forEach() due to its inability to exit early.

    I also often use [].map() and [].reduce() Array higher order methods; both provide succinct semantic solutions when working with arrays and their elements. These and the other Array higher order methods — accessors, mutators and iterators — provide a lot of functionality and can often eliminate the need to use an add-on library such as Underscore.

    Another excellent article, Ariya.

  • 紫云飞

    How about ES6′s Array.prototype.find

  • Chaz

    While interesting. The for loop still outperforms Array.prototype.some().

    http://jsperf.com/array-some-cgatian

    • En_joy

      I live for micro-optimization, but it isn’t necessary for most applications. This is easier to set up, and it’s closer to English-readable.

      • http://ariya.ofilabs.com/ Ariya Hidayat

        True. Functional style might be slower (for now, due to the lack of automatic vectorization in the implementation), but the goal is really readability and code quality.
        For performance-critical code, nothing beats a hand-written loop specifically designed to tackle the case.

    • tomByrer

      Thanks for the link Chaz; that is quite a perf gap.

  • skyboyer

    1. What about throwing an exception to exit iteration? Among semantically incorrect using of exception to exit from loop is there any “technical” objection?
    2. At least IE8 does not support .some() :( Fallbacks, fallbacks…
    3. Returning true to exit from the loop is confusing for a bit does not it? This way every + return false is better for a while.