In an array of objects, fastest way to find the index of an object whose attributes match a search

217,846

Solution 1

Maybe you would like to use higher-order functions such as "map". Assuming you want search by 'field' attribute:

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];

Solution 2

The simplest and easiest way to find element index in array.

ES5 syntax: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

ES6 syntax: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)

Solution 3

The new Array method .filter() would work well for this:

var filteredArray = array.filter(function (element) { 
    return element.id === 0;
});

jQuery can also do this with .grep()

edit: it is worth mentioning that both of these functions just iterate under the hood, there won't be a noticeable performance difference between them and rolling your own filter function, but why re-invent the wheel.

Solution 4

If you care about performance, dont go with find or filter or map or any of the above discussed methods

Here is an example demonstrating the fastest method. HERE is the link to the actual test

Setup block

var items = []

for(var i = 0; i < 1000; i++) {
    items.push({id: i + 1})
}

var find = 523

Fastest Method

var index = -1
for(var i = 0; i < items.length; i++) {
    if(items[i].id === find) {
        index = i;
        break;
    }
}

Slower Methods

items.findIndex(item => item.id === find)

SLOWEST method

items.map(item => item.id).indexOf(find);

Solution 5

Since there's no answer using regular array find:

var one = {id: 1, name: 'one'};
var two = {id: 2, name:'two'}
var arr = [one, two] 

var found = arr.find((a) => a.id === 2)

found === two // true

arr.indexOf(found) // 1
Share:
217,846

Related videos on Youtube

Petrov
Author by

Petrov

Javascript developer in Paris, liking all the shiny new tools (node.js, js mvc etc) floating around...

Updated on August 16, 2021

Comments

  • Petrov
    Petrov almost 3 years

    I've been surfing around a little trying to find an efficient way to do this, but have gotten nowhere. I have an array of objects that looks like this:

    array[i].id = some number;
    array[i].name = some name;
    

    What I want to do is to find the INDEXES of the objects where id is equal to, for example, one of 0,1,2,3 or 4. I suppose I could just do something like :

    var indexes = [];
    for(i=0; i<array.length; i++) {
      (array[i].id === 0) ? { indexes[0] = i }
      (array[i].id === 1) ? { indexes[1] = i }
      (array[i].id === 2) ? { indexes[2] = i }
      (array[i].id === 3) ? { indexes[3] = i }
      (array[i].id === 4) ? { indexes[4] = i }
    }
    

    While this would work, it looks to be quite expensive and slow (not to mention ugly), especially if array.length could be large. Any ideas on how to spruce this up a bit? I thought of using array.indexOf somehow but I don't see how to force the syntax. This

    array.indexOf(this.id === 0);
    

    for example, returns undefined, as it probably should.

    • Dave Newton
      Dave Newton about 12 years
      If you have a plain old array, all you can do is iterate. That's what arrays are, a bunch of objects ordered by array index.
    • Conrad Lo
      Conrad Lo over 7 years
      Just come across this post today, for all latecomers there is a new array method Array.prototype.findIndex() in ECMAScript 2015. The accepted answer was awesome tho.
    • Fr0zenFyr
      Fr0zenFyr about 5 years
      I'm a fan of ES6 syntax (use polyfills, if support on legacy browsers is needed). ES7+ES8 are going to be future
    • Worthy7
      Worthy7 over 3 years
      Just FYI, if you want to be able to quickly lookup then you should probably not use arrays, but use dictionaries instead (Id, object)
  • Tejs
    Tejs about 12 years
    +1, I always forget about built in functions like this on objects.
  • Adam Grant
    Adam Grant almost 11 years
    This doesn't return an index.
  • counterbeing
    counterbeing over 10 years
    This answer is great because it actually answers the question by providing the index :)
  • eKelvin
    eKelvin over 9 years
    That's Ok for one criteria but what if one need to compare more than one criteria?
  • Pablo Francisco Pérez Hidalgo
    Pablo Francisco Pérez Hidalgo over 9 years
    @ZeroAbsolute Your applied function (passed to map) can return a hash string which should provide a unique key for each possible combination given by your criteria. For example: function hashf(el) { return String(el.id) + "_" + String(el.name); }. This is just a hint: elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'})); Obviously, the hash function I provide is not valid for all cases since '_' could form part of your values but it is just a quick example you can figure out different hash methods.
  • Nathan C. Tresch
    Nathan C. Tresch over 9 years
    What does this return if it isn't found? I assume -1, just curious. I'll experiment.
  • Pablo Francisco Pérez Hidalgo
    Pablo Francisco Pérez Hidalgo over 9 years
    @NathanC.Tresch It returns -1 because that is indexOf return value when it can't locate a given value.
  • Nathan C. Tresch
    Nathan C. Tresch over 9 years
    @PabloFranciscoPérezHidalgo Yeah, I assumed that was it. Thanks for the response and the answer, it works well.
  • Rick
    Rick almost 9 years
    A quick example to copy-paste for you to play with Pablo's simple solution to this very common problem! var arrayOfObjects = [ { a: 1, aa: "two"}, // 0 { a: "asdf", aa: 2}, // 1 { a: "cat", aa: "dog"} // 2 ]; var elementPos = arrayOfObjects.map(function(x) {return x.aa; }).indexOf('dog'); var objectFound = arrayOfObjects[elementPos]; console.log(elementPos, objectFound); // 2 { a: 'cat', aa: 'dog' }
  • rochasdv
    rochasdv over 8 years
    This doesn't answer this specific question, but help me a lot! Thanks!
  • Umair Ahmed
    Umair Ahmed over 7 years
    Hi everyone instead of using two methods map, indexOf you can use just one called findIndex....... Ex: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3}) OR [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)
  • mrogers
    mrogers over 7 years
    I believe this is the most elegant solution. For those worried about backwards compatibility you can find the polyfill for findIndex at developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
  • Ioannis K. Moutsatsos
    Ioannis K. Moutsatsos almost 7 years
    Works only if the item appears once. If the item is present more than once, the indexOf method returns the position of just the first occurrence. So it will not provide an array with all of the indices that the item is present
  • mjwrazor
    mjwrazor over 6 years
    for looping over 5 million single object sized items the time difference for checking in a forEach loop is marginal
  • Rich
    Rich over 6 years
    This doesn't return the index.
  • patotoma
    patotoma over 6 years
    also there is a tiny utility for this called super-array
  • Martijn Pieters
    Martijn Pieters over 6 years
    You may want to read How to offer personal open-source libraries? before posting this everywhere.
  • patotoma
    patotoma over 6 years
    @MartijnPieters I've posted it only to a few relevant questions and the project is MIT free so what's the deal? Maybe you could be a bit more tolerating.
  • thclark
    thclark almost 6 years
    I get a warning in my ES6 lint tool that the obj.id == 3 operator used here can cause unexpected type conversion, so use the obj.id === 3 operator instead, which tests for equal value and type.
  • Iain Collins
    Iain Collins about 5 years
    Thanks for providing this comparison! What's super interesting is how much performance varies - including which method is faster varying depending on what browser / JavaScript engine used to run them.
  • Ovidio Reyna
    Ovidio Reyna almost 5 years
    This answer is at least 3.5 times faster than the accepted answer above. Using var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); it took 0.03500000002532033 milliseconds Using [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3) it took 0.00999999747378752 milliseconds.
  • Karun
    Karun almost 5 years
    THIS ANSWER is the most EFFICIENT since it doesn't iterate the whole array. The selected answer will map complete array and then findIndex which is bound to iterate through whole array once
  • Painkiller
    Painkiller about 4 years
    I think this should be marked as an answer. This shows the fastest way and slower ones.
  • Admin
    Admin about 4 years
    In your benchmark, block 2 (using findIndex) actually is faster for me (on Microsoft Edge Chromium 83.0.474.0)
  • cody mikol
    cody mikol about 4 years
    Block 2 is now faster on chrome as well
  • Günay Gültekin
    Günay Gültekin over 3 years
    I have extend the benchmark jsben.ch/19PxA and this 4th block is the fastest in Chrome. -> items.map(function(x) {return x.id; }).indexOf(find);
  • TamusJRoyce
    TamusJRoyce over 3 years
    What speeds does items.findIndex(item => item.id === find) run using Babel and webpack or parcel -> ES5 (example: under a typical typescript environment)?
  • Parth Developer
    Parth Developer over 3 years
    This is the correct way to get INDEX const index = this.pages.findIndex(object => { return object.id === id; }); console.log('index', index);
  • arled
    arled about 3 years
    This answer is not the best and does not fully answer the question @counterbeing this is definitely not the fastest way. map() is extremely slow with large arrays. consider using new Set() or some() etc.
  • mak
    mak over 2 years
    map is slow. A good old for (inverted) loop and break on hit is the way to go.
  • Shinoy Babu
    Shinoy Babu over 2 years
    var elementPos = array.map((x=>x.id).indexOf(idYourAreLookingFor); var objectFound = array[elementPos];