Stack Overflow is a community of 4.7 million programmers, just like you, helping each other.

Join them; it only takes a minute:

Sign up
Join the Stack Overflow community to:
  1. Ask programming questions
  2. Answer and help your peers
  3. Get recognized for your expertise

I am trying to find the index of an object within an array. I know there is a way to do this with underscore.js but I am trying to find an efficient way without underscore.js. Here is what I have :

var arrayOfObjs = [{
  "ob1": "test1"
}, {
  "ob2": "test1"
}, {
  "ob1": "test3"
}];

function FindIndex(key) {
  var rx = /\{.*?\}/;            // regex: finds string that starts with { and ends with }
  var arr = [];                  // creates new array
  var str = JSON.stringify(arrayOfObjs);          // turns array of objects into a string
  for (i = 0; i < arrayOfObjs.length; i++) {      // loops through array of objects
    arr.push(str.match(rx)[0]);                   // pushes matched string into new array
    str = str.replace(rx, '');                    // removes matched string from str
  }
  var Index = arr.indexOf(JSON.stringify(key));   // stringfy key and finds index of key in the new array
  alert(Index);
}

FindIndex({"ob2": "test1"});

JSFIDDLE

This works but I am afraid it isn't very efficient. Any alternatives?

share|improve this question
    
do you need it just once or more than once? – Nina Scholz yesterday
3  
You could, but it won't be any more efficient, it's still javascript – adeneo yesterday
1  
If comparing the stringified objects is enough, you could at least drop all the regex stuff, and just iterate -> jsfiddle.net/g30myfnh/1 – adeneo yesterday
1  
@Anthony That sounds like a better idea if you're able to do that. If you need to look up these objects and get their index (key) often, hash table would be a great solution. Hashtables are also surprisingly easy to write in JavaScript too, since everything in JavaScript is basically build off a hashtable already – Nick Zuber yesterday
1  
@Anthony Also, as a side note, when it comes to speed efficiency always try to avoid JSON.stringify(); that method is incredibly slow – Nick Zuber yesterday

Here's one way to do it, somewhat reliably and a little more efficiently, using some() and stopping as soon as the objects don't match etc.

var arrayOfObjs = [{
  "ob1": "test1"
}, {
  "ob2": "test1"
}, {
  "ob1": "test3"
}];

function FindIndex(key) {
    var index = -1;

    arrayOfObjs.some(function(item, i) {
    	var result = Object.keys(key).some(function(oKey) {
            return (oKey in item && item[oKey] === key[oKey]);
        });
        if (result) index = i;
        return result;
    });
    
    return index;
}

var index = FindIndex({"ob2": "test1"});

document.body.innerHTML = "'{\"ob2\": \"test1\"}' is at index : " + index;

share|improve this answer
    
When you say somewhat reliably, do you mean it may not work in every case? – Anthony yesterday
1  
@Anthony - It works in all cases where the values are primitives, and probably for other values as well, but probably not where the values are functions, prototypes or other objects etc. Comparing objects with any kind of data can be complicated, this is a quick fix for most "regular" objects. – adeneo yesterday
    
Just curious: Is there a reason you use arrayOfObjs.some and assign an index instead of just doing a for loop and returning the index immediately? – Mike C yesterday
    
The first some() stops as soon as a matching object is found, the second some() stops as soon as a key or value doesn't match, so as to not waste time iterating on when an object doesn't match because it doesn't have the key, or the value isn't the same, or for the outer some() when a matching object is already found. – adeneo yesterday
1  
@MikeC - indeed it would, and the inner loop could use a break and a regular for loop, would probably be even more efficient in many browsers, as some() isn't very fast in some browsers – adeneo yesterday

A hash table with an example of access.

var arrayOfObjs = [{ "obj1": "test1" }, { "obj2": "test1" }, { "obj1": "test3" }],
    hash = {};

arrayOfObjs.forEach(function (a, i) {
    Object.keys(a).forEach(function (k) {
        hash[k] = hash[k] || {};
        hash[k][a[k]] = i;
    });
});

document.write('<pre>' + JSON.stringify(hash['obj2']['test1'], 0, 4) + '</pre>');
document.write('<pre>' + JSON.stringify(hash, 0, 4) + '</pre>');

share|improve this answer
1  
How does this help them get the index of the matching object in arrayOfObjs? – Mike C yesterday
    
@MikeC, now -- by now. – Nina Scholz yesterday
1  
Note that you're relying on order in objects – adeneo yesterday
    
actually there is only one property. in RL, i would use a array with a fixed order for the properties. or a different approach. ;) – Nina Scholz yesterday
1  
This would certainly be faster for lookups than anything else, and is by far the most efficient way, and I think I misread it at first, as there wouldn't be an issue with order in objects when you're storing the index from the outer iteration over the array. – adeneo yesterday

One way of doing this would be to use every to see if each key in the "filter" has a matching, correct value in an object. every ensures that the loop stops as soon as it finds a mismatched or missing value.

function log(msg) {
  document.querySelector('pre').innerHTML += msg + '\n';
}

var arr = [
  {
    a: 1
  },
  {
    b: 2
  },
  {
    c: 3,
    d: 4
  },
  {
    a: 1 // Will never reach this since it finds the first occurrence
  }
];

function getIndex(filter) {
  var keys = Object.keys(filter);
  for (var i = 0, len = arr.length; i < len; i++) {
    var obj = arr[i];
    var match = keys.every(function(key) {
      return filter[key] === obj[key];
    });
    if (match) {
      return i;
    }
  }
  
  return -1;
}

log(getIndex({ a: 1 }));
log(getIndex({ b: 2 }));
log(getIndex({ c: 3 }));
log(getIndex({ c: 3, d: 4 }));
log(getIndex({ e: 5 })); // Doesn't exist, won't find it
<pre></pre>

share|improve this answer

For an alternative to your customly built approach, lodash's findIndex method does exactly this for you:

var arrayOfObjs = [{
  "ob1": "test1"
}, {
  "ob2": "test1"
}, {
  "ob1": "test3"
}];

_.findIndex(arrayOfObjs, {"ob2": "test1"}); // => 1
share|improve this answer

Since testing equality on two different objects will always return false you could first test keys and then values ,

using reduce :

var arrayOfObjs = [{
  "ob1": "test1"
}, {
  "ob2": "test1" , k2:2
}, {
  "ob1": "test3"
}];

function getI( obj, arr){
 const checkK= Object.keys(obj);
 return arr.reduce((ac,x,i) => {
  if ( checkK.every(z =>  x[z] && obj[z] === x[z]) )
    ac.push(i);
  return ac;
  },[])
}

document.write( 'result is :'+ getI({ob2:'test1', k2:2},arrayOfObjs))

share|improve this answer

findIndex won't work in old browsers, but was designed for this specific purpose.

var arrayOfObjs = [{
  "ob1": "test1"
}, {
  "ob2": "test1"
}, {
  "ob1": "test3"
}];

function FindIndex(key) {
  return arrayOfObjs.findIndex(
    obj => Object.keys(key).every(name => key[name] === obj[name])
  );
}

alert(FindIndex({"ob2": "test1"})); // 1

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.