Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there an indexOf in javascript to search an array with custom compare function

I need the index of the first value in the array, that matches a custom compare function.

The very nice underscorej has a "find" function that returns the first value where a function returns true, but I would need this that returns the index instead. Is there a version of indexOf available somewhere, where I can pass a function used to comparing?

Thanks for any suggestions!

like image 535
Peter T. Avatar asked Sep 10 '12 17:09

Peter T.


People also ask

Can you use indexOf for an array?

Introduction to the JavaScript array indexOf() method To find the position of an element in an array, you use the indexOf() method. This method returns the index of the first occurrence the element that you want to find, or -1 if the element is not found.

Can you use indexOf on an array of objects JavaScript?

To find the index of an object in an array, by a specific property: Use the map() method to iterate over the array, returning only the value of the relevant property. Call the indexOf() method on the returned from map array. The indexOf method returns the index of the first occurrence of a value in an array.

What is the difference between findIndex and indexOf?

findIndex - Returns the index of the first element in the array where predicate is true, and -1 otherwise. indexOf - Returns the index of the first occurrence of a value in an array.

Which is better indexOf or includes?

I would suggest using the includes() method to check if the element is present in the array. If you need to know where the element is in the array, you need to use the indexOf() method.


7 Answers

Here's the Underscore way to do it - this augments the core Underscore function with one that accepts an iterator function:

// save a reference to the core implementation
var indexOfValue = _.indexOf;

// using .mixin allows both wrapped and unwrapped calls:
// _(array).indexOf(...) and _.indexOf(array, ...)
_.mixin({

    // return the index of the first array element passing a test
    indexOf: function(array, test) {
        // delegate to standard indexOf if the test isn't a function
        if (!_.isFunction(test)) return indexOfValue(array, test);
        // otherwise, look for the index
        for (var x = 0; x < array.length; x++) {
            if (test(array[x])) return x;
        }
        // not found, return fail value
        return -1;
    }

});

_.indexOf([1,2,3], 3); // 2
_.indexOf([1,2,3], function(el) { return el > 2; } ); // 2
like image 198
nrabinowitz Avatar answered Sep 29 '22 11:09

nrabinowitz


There's a standard function in ECMAScript 2015 for Array.prototype.findIndex(). Currently it's implemented in all major browsers apart from Internet Explorer.

Here's a polyfill, courtesy of the Mozilla Developer Network:

// https://tc39.github.io/ecma262/#sec-array.prototype.findIndex
if (!Array.prototype.findIndex) {
  Object.defineProperty(Array.prototype, 'findIndex', {
    value: function(predicate) {
     // 1. Let O be ? ToObject(this value).
      if (this == null) {
        throw new TypeError('"this" is null or not defined');
      }

      var o = Object(this);

      // 2. Let len be ? ToLength(? Get(O, "length")).
      var len = o.length >>> 0;

      // 3. If IsCallable(predicate) is false, throw a TypeError exception.
      if (typeof predicate !== 'function') {
        throw new TypeError('predicate must be a function');
      }

      // 4. If thisArg was supplied, let T be thisArg; else let T be undefined.
      var thisArg = arguments[1];

      // 5. Let k be 0.
      var k = 0;

      // 6. Repeat, while k < len
      while (k < len) {
        // a. Let Pk be ! ToString(k).
        // b. Let kValue be ? Get(O, Pk).
        // c. Let testResult be ToBoolean(? Call(predicate, T, « kValue, k, O »)).
        // d. If testResult is true, return k.
        var kValue = o[k];
        if (predicate.call(thisArg, kValue, k, o)) {
          return k;
        }
        // e. Increase k by 1.
        k++;
      }

      // 7. Return -1.
      return -1;
    },
    configurable: true,
    writable: true
  });
}
like image 26
Husky Avatar answered Sep 26 '22 11:09

Husky


You could do something like this:

Array.prototype.myIndexOf = function(f)
{
    for(var i=0; i<this.length; ++i)
    {
        if( f(this[i]) )
            return i;
    }
    return -1;
};

Regarding Christian's comment: if you override a standard JavaScript method with a custom one with a different the same signature and different functionality, bad thing will likely happen. This is especially true if you're pulling in 3rd party libraries which may depend on the original, say, Array.proto.indexOf. So yeah, you probably want to call it something else.

like image 24
Nathan Andrew Mullenax Avatar answered Sep 26 '22 11:09

Nathan Andrew Mullenax


As others have noted, easy enough to roll your own, which you can keep short and simple for your particular use case:

// Find the index of the first element in array
// meeting specified condition.
//
var findIndex = function(arr, cond) {
  var i, x;
  for (i in arr) {
    x = arr[i];
    if (cond(x)) return parseInt(i);
  }
};

var moreThanTwo = function(x) { return x > 2 }
var i = findIndex([1, 2, 3, 4], moreThanTwo)

Or if you're a CoffeeScripter:

findIndex = (arr, cond) ->
  for i, x of arr
    return parseInt(i) if cond(x)
like image 28
joyrexus Avatar answered Sep 28 '22 11:09

joyrexus


The javascript array method filter returns a subset of the array that return true from the function passed.

var arr= [1, 2, 3, 4, 5, 6],
first= arr.filter(function(itm){
    return itm>3;
})[0];
alert(first);

if you must support IE before #9 you can 'shim' Array.prototype.filter-

Array.prototype.filter= Array.prototype.filter || function(fun, scope){
    var T= this, A= [], i= 0, itm, L= T.length;
    if(typeof fun== 'function'){
        while(i<L){
            if(i in T){
                itm= T[i];
                if(fun.call(scope, itm, i, T)) A[A.length]= itm;
            }
            ++i;
        }
    }
    return A;
}
like image 30
kennebec Avatar answered Sep 28 '22 11:09

kennebec


How about such find function ?

(function () {
  if (!Array.prototype._find) {
    Array.prototype._find = function (value) {
      var i = -1, j = this.length;
      if (typeof(value)=="function") 
         for(; (++i < j) && !value(this[i]););
      else
         for(; (++i < j) && !(this[i] === value););

      return i!=j ? i : -1;
    }
  }
}());
like image 20
23W Avatar answered Sep 26 '22 11:09

23W


Here comes the coffeescript version of nrabinowitz's code.

# save a reference to the core implementation
indexOfValue = _.indexOf

# using .mixin allows both wrapped and unwrapped calls:
# _(array).indexOf(...) and _.indexOf(array, ...)
_.mixin ({
    # return the index of the first array element passing a test
    indexOf: (array, test) ->
        # delegate to standard indexOf if the test isn't a function
        if (!_.isFunction(test))
            return indexOfValue(array, test)
        # otherwise, look for the index
        for item, i in array
            return i if (test(item))
        # not found, return fail value
        return -1
})
like image 24
Shiva Huang Avatar answered Sep 28 '22 11:09

Shiva Huang