696

How can I simply and directly find the index within an array of objects meeting some condition?

For example, given this input:

var hello = { hello: 'world', foo: 'bar'};
var qaz = { hello: 'stevie', foo: 'baz'}

var myArray = [];
myArray.push(hello, qaz);

How can I search myArray to find the index of the element whose hello property equals 'stevie' (in this case, the result should be 1)?

surfmuggle
  • 5,527
  • 7
  • 48
  • 77
Antonio Laguna
  • 8,973
  • 7
  • 36
  • 72
  • 1
    Do you want to merge the two objects `hello` and `qaz`? – Armin Dec 29 '11 at 13:01
  • Nope I don't. I want to have a list of objects in an array. – Antonio Laguna Dec 29 '11 at 13:05
  • Ah okay! You want to know the position of the whole object in the array, which has a defined property. – Armin Dec 29 '11 at 13:06
  • 13
    I found a very simple function to solve this exact problem with this SO answer: `var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos];` [link] (http://stackoverflow.com/a/16100446/1937255) – Rick Jul 30 '15 at 00:21
  • 1
    ES6 Array.indexOf is better than accepted answer (if ES6 works for you) - see full example below – yar1 Jan 31 '17 at 06:44

29 Answers29

1351

I think you can solve it in one line using the map function:

const pos = myArray.map(e => e.hello).indexOf('stevie');
leonheess
  • 16,068
  • 14
  • 77
  • 112
  • 65
    This should honestly be the accepted answer. Most browsers nowadays support `Array.prototype.map()` – AlbertEngelB Jun 27 '13 at 21:06
  • 11
    It's not supported by IE8 but, if that's not a problem, this is the best solution. – Antonio Laguna Nov 22 '13 at 11:56
  • 1
    This is without a doubt the best answer in 2014. Array.prototype.map() is supported through IE9 now. https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map – rjhilgefort Mar 14 '14 at 00:59
  • This one is clean and sweet. For modern, mobile browser controls to b e used in (hybrid) apps on iOS and Android .map is supported. If i'd make this for normal browsers i'd go with tandrewnichols answer and use Underscore.js's implementation... – EeKay Sep 08 '14 at 11:52
  • Elegant answer. Array.prototype.map() is soported for IE9+ and you can use a simple Polyfill for IE8, 7, 6: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill – Johann Echavarria Sep 30 '14 at 15:05
  • I use this method often but it's worth noting that indexOf will also accept an object reference so if you don't need to compare by a specific property (like OP) you can just do `var index = items.indexOf(item)` to get the correct index. Here's example: http://plnkr.co/edit/ZdShIA?p=preview – parliament Dec 02 '14 at 03:12
  • 84
    Um... isn't it worth noting that Array.prototype.map() creates a whole new array containing the mapped items? So, if you've got an array with 1000 elements, you've created *another* array with 1000 elements first, then search it? It would be worthwhile I think to see the performance of this method vs. a simple for loop. Especially when you're running on a mobile platform with limited resources. – Doug Aug 17 '15 at 05:51
  • 8
    @Doug Although your point about performance is indeed correct, who in their right mind would replace one line of code with seven for an application that is almost by definition IO/Network bound until they'd profiled for bottlenecks? – Jared Smith Nov 04 '15 at 17:12
  • 1
    @JaredSmith Just trying to keep people forward-thinking. I'm not saying this is a _bad_ solution; but, with ES6 generator functions and newer alternatives, there are a lot more performant (and clean) solutions out there. http://www.aaron-powell.com/posts/2013-12-31-linq-in-javascript-for-real.html – Doug Nov 04 '15 at 21:51
  • 1
    @Doug better indeed :). If you want a better performing but equally terse ES 5 version Esailija's answer below, while still O(n) is faster than this one by at least a factor of 2. – Jared Smith Nov 05 '15 at 00:54
  • 1
    For the sake of the argument, here is a JSPerf comparing using map + indexOf vs ES2015 findIndex. http://jsperf.com/map-indexof-vs-findindex (Spoiler alert: findIndex is 50% faster). It doesn't work on IE though, but you can polyfill it, who cares! – Zequez Feb 11 '16 at 04:41
  • 2
    Quick version: `pos = myArray.map((e) => e.hello).indexOf('stevie');` – Tom Feb 26 '17 at 12:35
  • 19
    Technically a minified js file is also one line ;D – greaterKing May 03 '17 at 15:45
  • @Zequez Neither this answer, nor the findIndex is the most performant answer. Based on my benchmarks (see jsperf.com/find-index-of-object-in-array-by-contents ), the for loop in a prototype actually ends up with the highest performance (more than twice as fast), as mentioned in [my answer](https://stackoverflow.com/questions/8668174/indexof-method-in-an-object-array/44790688#44790688), and also ends up being the most terse for repetition. – Uniphonic Jun 27 '17 at 23:26
  • My only concern is readability, very difficult to follow what its doing. One liners are impressive to fellow experienced developers, but not easy to follow and maintain for others. – RollingInTheDeep Jul 16 '19 at 09:31
  • @Doug To some people using Array.map looks more *expertish* – Muhammad bin Yusrat Jul 24 '19 at 05:31
  • This answer doesn't work if multiple objects in the array have a `hello` property with a value of `stevie` .... – James B Dec 12 '19 at 22:30
  • so stevie is a pos huh? – Nikki Luzader Jun 02 '20 at 23:21
  • 1
    THANK U <3 so much – Zack Heisenberg Jun 06 '20 at 16:17
  • This has the unfortunate side affect of reducing the objects in the array thought :( – user959690 Aug 17 '23 at 21:28
518

Array.prototype.findIndex is supported in all browsers other than IE (non-edge). But the polyfill provided is nice.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

The solution with map is okay. But you are iterating over the entire array every search. That is only the worst case for findIndex which stops iterating once a match is found.


There's not really a concise way (when devs had to worry about IE8), but here's a common solution:
var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

or as a function:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Just some notes:

  1. Don't use for...in loops on arrays
  2. Be sure to break out of the loop or return out of the function once you've found your "needle"
  3. Be careful with object equality

For example,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found
Joe
  • 80,724
  • 18
  • 127
  • 145
  • the function has the searchterm comparation wrong as it should be searchTerm :) – Antonio Laguna Dec 29 '11 at 14:50
  • there were multiple occurences – Joe Feb 27 '14 at 21:06
  • 3
    @SteveBennett it is a performance optimized version; the length of the array has to be determined only once (when the variables for the for-loop are initialized). In your case, the length is checked every iteration anew. See also http://stackoverflow.com/questions/5349425/whats-the-fastest-way-to-loop-through-an-array-in-javascript and http://stackoverflow.com/questions/8452317/do-loops-check-the-array-length-every-time-when-comparing-i-against-array-length However, if performance is not high prio it doesn't really matter. – loother Apr 20 '16 at 15:09
  • 1
    Good answer, but I did some performance benchmarking (see https://jsperf.com/find-index-of-object-in-array-by-contents ), and found that the function based answer mentioned here seems to be the second most performant answer. The only thing more performant ends up being putting it into a prototype, instead of just a function, as mentioned in [my answer](https://stackoverflow.com/questions/8668174/indexof-method-in-an-object-array/44790688#44790688). – Uniphonic Jun 27 '17 at 23:33
  • Polyfill link is dead! – Mehdi Dehghani Jan 29 '22 at 10:00
152

In ES2015, this is pretty easy:

myArray.map(x => x.hello).indexOf('stevie')

or, probably with better performance for larger arrays:

myArray.findIndex(x => x.hello === 'stevie')
Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
  • 2
    Good approach to use ES6 – kag Sep 20 '16 at 06:36
  • I was surprised that neither of these methods is as performant as the prototyped for loop, as mentioned in my answer. Even though the findIndex method browser support is a bit poor, it seems like it would be doing the same thing, but still ends up less performant? See the link in my answer for benchmarks. – Uniphonic Jun 27 '17 at 22:32
  • Good to know, if performance is important for the task at hand. It very rarely is, in my experience, but ymmv. – Steve Bennett Jun 29 '17 at 03:49
  • @Uniphonic - year 2021 findIndex if supported by all browsers, except OperaMini and IE - https://caniuse.com/array-find-index – Mauricio Gracia Gutierrez Jun 30 '21 at 13:37
25
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );
Esailija
  • 138,174
  • 23
  • 272
  • 326
17

I like Pablo's answer, but Array#indexOf and Array#map don't work on all browsers. Underscore will use native code if it's available, but has fallbacks as well. Plus it has the pluck method for doing exactly what Pablo's anonymous map method does.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();
tandrewnichols
  • 3,456
  • 1
  • 28
  • 33
  • 1
    Array.prototype.map() is soported for IE9+ and you can use a Polyfill for IE8, 7, 6: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map#Polyfill – Johann Echavarria Sep 30 '14 at 15:02
  • 1
    You could use a polyfill . . . _or you could just use underscore or lodash, which are basically polyfills that have a whole bunch of other goodies attached_. What's the objection with underscore? Size? – tandrewnichols Oct 01 '14 at 11:45
  • I really like Underscore, your answer is clever too, but IMHO Pablo's answer is the cleanest. – Johann Echavarria Oct 02 '14 at 05:35
  • Wow I never thought to use chaining like that. I really like how it makes the search fluent. – Dylan Pierce Apr 20 '16 at 20:12
  • `chain` is superfluous here. `_.pluck(myArray, 'hello').indexOf('stevie')` – Steve Bennett Aug 15 '18 at 06:58
15

Or prototype it :

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");
Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
Nathan Zaetta
  • 395
  • 3
  • 7
  • 9
    Very convenient! Although I would choose prototype.indexOfObject so as not to interfere with the exisitng Array.indexOf method. `Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };` – Adam Oct 30 '13 at 11:42
  • 1
    I would wrap it in a self executing closure with the old being stored beforehand, with the first line of the replacement function being something along the lines of `if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);`. This is because these are the few types that are immutable. I would also feature a third argument to enable fallback to the native method if needed. – Claudia May 27 '14 at 04:52
10

While, most other answers here are valid. Sometimes, it's best to just make a short simple function near where you will use it.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

It depends on what is important to you. It might save lines of code and be very clever to use a one-liner, or to put a generic solution somewhere that covers various edge cases. But sometimes it's better to just say: "here I did it like this" rather than leave future developers to have extra reverse engineering work. Especially if you consider yourself "a newbie" like in your question.

SpiRail
  • 1,377
  • 19
  • 28
9

Brief

myArray.indexOf('stevie','hello')

Use Cases :

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API :

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };
Abdennour TOUMI
  • 87,526
  • 38
  • 249
  • 254
9

If your object is the same object of the ones you are using within the array, you should be able to get the index of the Object in the same way you do as if it was a string.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1
Caio Koiti
  • 89
  • 1
  • 3
  • 1
    This is the answer I was looking for as it was unclear to me if IndexOf matched by value or what. Now I know I can use IndexOf to find my object, and not worry if there are other objects with the same properties. – MDave May 10 '20 at 00:13
9

I compared several methods and received a result with the fastest way to solve this problem. It's a for loop. It's 5+ times faster than any other method.

Here is the test's page: https://jsbench.me/9hjewv6a98

John Klimov
  • 161
  • 2
  • 4
8

you can use findIndex() method:

cosnt myIndex=myArray.findIndex(el=>el.hello==='stevie')

if myIndex < 0 that means is not exist

6

I did some performance testing of various answers here, which anyone can run them self:

https://jsperf.com/find-index-of-object-in-array-by-contents

Based on my initial tests in Chrome, the following method (using a for loop set up inside a prototype) is the fastest:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

This code is a slightly modified version of Nathan Zaetta's answer.

In the performance benchmarks I tried it with both the target being in the middle (index 500) and very end (index 999) of a 1000 object array, and even if I put the target in as the very last item in the array (meaning that it it has to loop through every single item in the array before it's found) it still ends up the fastest.

This solution also has the benefit of being one of the most terse for repeatedly executing, since only the last line needs to be repeated:

myArray.indexOfObject("hello", "stevie");
Uniphonic
  • 845
  • 12
  • 21
  • 2
    I was just about to post an answer to this question using a fiddle with my own tests, but thanks to your answer, I don't have to anymore. I just want to confirm your tests - I got to the same result, but using a `while` loop instead of a `for` one, and `performance.now()`. I'd wish that this answer was upvoted more and that I saw it earlier, it would have saved me some time... – Yin Cognyto Dec 05 '17 at 15:48
4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Mind the [0].

It is proper to use reduce as in Antonio Laguna's answer.

Apologies for the brevity...

Cody
  • 9,785
  • 4
  • 61
  • 46
4

Try this:

console.log(Object.keys({foo:"_0_", bar:"_1_"}).indexOf("bar"));

https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

John Doe
  • 41
  • 1
4

simple:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])
CherryDT
  • 25,571
  • 5
  • 49
  • 74
griffon vulture
  • 6,594
  • 6
  • 36
  • 57
3

You can use a native and convenient function Array.prototype.findIndex() basically:

The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.

Just a note it is not supported on Internet Explorer, Opera and Safari, but you can use a Polyfill provided in the link below.

More information:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);
GibboK
  • 71,848
  • 143
  • 435
  • 658
3

If you are only interested into finding the position see @Pablo's answer.

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

However, if you are looking forward to finding the element (i.e. if you were thinking of doing something like this myArray[pos]), there is a more efficient one-line way to do it, using filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

See perfomance results (~ +42% ops/sec): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3

Community
  • 1
  • 1
zurfyx
  • 31,043
  • 20
  • 111
  • 145
2

I have made a generic function to check the below is the code & works for any object

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

The first alert will return -1 (means match not found) & second alert will return 0 (means match found).

Shiljo Paulson
  • 518
  • 7
  • 17
2

Use _.findIndex from underscore.js library

Here's the example _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1

Steve Bennett
  • 114,604
  • 39
  • 168
  • 219
niren
  • 2,693
  • 8
  • 34
  • 58
2

Using the ES6 findIndex method, without lodash or any other libraries, you can write:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

This will compare the immediate properties of the object, but not recurse into the properties.

If your implementation doesn't provide findIndex yet (most don't), you can add a light polyfill that supports this search:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(from my answer on this dupe)

Community
  • 1
  • 1
ssube
  • 47,010
  • 7
  • 103
  • 140
2

Furthor of @Monika Garg answer, you can use findIndex() (There is a polyfill for unsupprted browsers).

I saw that people downvoted this answer, and I hope that they did this because of the wrong syntax, because on my opinion, this is the most elegant way.

The findIndex() method returns an index in the array, if an element in the array satisfies the provided testing function. Otherwise -1 is returned.

For example:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);
Community
  • 1
  • 1
Mosh Feu
  • 28,354
  • 16
  • 88
  • 135
2

See this example: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

It starts to count with zero.

Armin
  • 15,582
  • 10
  • 47
  • 64
1

This is the way to find the object's index in array

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }
Asad Fida
  • 216
  • 2
  • 6
1

This works without custom code

var arr, a, found;
arr = [{x: 1, y: 2}];
a = {x: 1, y: 2};
found = JSON.stringify(arr).indexOf(JSON.stringify(a)) > - 1;
// found === true

Note: this does not give the actual index, it only tells if your object exists in the current data structure

Xeltor
  • 4,626
  • 3
  • 24
  • 26
0

You can simply use

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

No underscore, no for, just a reduce.

7ynk3r
  • 958
  • 13
  • 17
0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));
kbariotis
  • 783
  • 1
  • 12
  • 25
0

Most answers response here do not resolve all cases. I found this solution better:

const isInarray = myArr.filter((obj) => obj.hello === 'stevie' && obj.foo === 'baz').length > 0;
if (!isInArray) {
 ....
}
lvndry
  • 421
  • 6
  • 13
-1

You can create your own prototype to do this:

something like:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}
Janx from Venezuela
  • 1,147
  • 1
  • 10
  • 12
  • 2
    Bad practice, breaking encapsulation: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Inheritance_and_the_prototype_chain#Bad_practice.3A_Extension_of_native_prototypes – HMR Apr 17 '14 at 23:27
  • This also would break on recursively defined objects. – Joseph Coco Jun 02 '15 at 16:59
-2

I will prefer to use findIndex() method:

 var index = myArray.findIndex('hello','stevie');

index will give you the index number.

Leigh
  • 12,038
  • 4
  • 28
  • 36
  • 1
    Answer , Spellings and Code indentation and :) are wrong? – Sachin Verma Feb 04 '14 at 06:16
  • 1
    findIndex is not in any javascript standard implementation. There is an upcoming (ecma 6) proposal for such a method, but its signature is not like that. Please, clarify what you mean (maybe the name of the method), give the findIndex method declaration or name the library you are using. – Sebastien F. Mar 22 '14 at 18:19