Array.prototype.find

JavaScript polyfill, by , Saturday, April 12th, 2014

In this article, I am going to show you three possible polyfills for the Array.prototype.find method. The method is part of ECMAScript 6, and can be used to find or test a specific element in an array, and it takes two parameters;

  • callback; a function that should return a Boolean value. The function will be called with three arguments and a possible this value. The arguments are;
    • value; the value at the current position of the array.
    • index; the current index number.
    • array; the array being iterated over.
  • thisArg; this value to use in the execution of the callback function. If no one is provided, undefined is used.

The way the method works is, that it returns an array value, if it satisfies the conditions set in the callback function. If no elements live up to the test, undefined is returned. You can think of the find method as a compliment to the Array.prototype.filter method, which instead of return a single element returns an array of elements that satisfies the conditions of the callback function. So with this knowleged lets have a look at the first polyfill;

Array.prototype.find polyfill with for-loop

This first polyfill is written using a simple for-loop, and should be straight forward to understand, so lets just present the code;

  1. if (!Array.prototype.find) {
  2. Array.prototype.find = function (callback, thisArg) {
  3. "use strict";
  4. var arr = this,
  5. arrLen = arr.length,
  6. i;
  7. for (i = 0; i < arrLen; i += 1) {
  8. if (callback.call(thisArg, arr[i], i, arr)) {
  9. return arr[i];
  10. }
  11. }
  12. return undefined;
  13. };
  14. }

If you want to be more careful with your code, you can add some safety checks in the code above. The first thing you need to check is if the this value is in fact an array. If it is not, you can throw a TypeError. The same goes for the callback function; you could make a check to see if it is a function, and if not, then again throw a TypeError. The code could look like this;

  1. if (Object.prototype.toString.call(arg) !== '[object Array]') {
  2. throw new TypeError('Array.prototype.find was called on a none array element');
  3. }
  4. if (typeof callback !== 'function') {
  5. throw new TypeError('callback is not a function');
  6. }

Array.prototype.find polyfill with forEach

Even though the previous polyfill works just fine, it can be simplified using the Array.prototype.forEach method. If your users make use of older browsers like IE8, you might want to use a polyfill for the forEach method as well. Here is a link to my Array.prototype.forEach polyfill. Now lets see how this version of the polyfill looks like;

  1. if (!Array.prototype.find) {
  2. Array.prototype.find = function (callback, thisArg) {
  3. "use strict";
  4. this.forEach(function (elmVal, i, arr) {
  5. if (callback.call(thisArg, elmVal, i, arr)) {
  6. return elmVal;
  7. }
  8. });
  9. return undefined;
  10. };
  11. }

It is pretty obvious that this is much simpler and more appealing provided you are sure that forEach is supported in your users environments.

Array.prototype.find with recursion

Another way to write this polyfill is with recursion. Anywhere where you see iteration, you can think if maybe recursion is a good alternative to using for-loops, so lets try that here;

  1. if (!Array.prototype.find) {
  2. Array.prototype.find = function (callback, thisArg) {
  3. "use strict";
  4. return (function checkNext(elmVal, i, arr) {
  5. return callback.call(thisArg, elmVal, i, arr) ? elmVal : (i < arr.length) ? checkNext(arr[i], i + 1, arr) : undefined;
  6. }(this[0], 0, this));
  7. };
  8. }

This recursive version is very compact but still pretty easy to read and understand. Which one of these you choose to try out, is of course up to you, so happy coding and enjoy the feature.