Why Array.indexOf doesn't find identical looking objects

JavascriptArraysSearchObjectIndexof

Javascript Problem Overview


I have array with objects.

Something Like this:

var arr = new Array(
  {x:1, y:2},
  {x:3, y:4}
);

When I try:

arr.indexOf({x:1, y:2});

It returns -1.

If I have strings or numbers or other type of elements but object, then indexOf() works fine.

Does anyone know why and what should I do to search object elements in array?

Of course, I mean the ways except making string hash keys for objects and give it to array...

Javascript Solutions


Solution 1 - Javascript

>indexOf compares searchElement to elements of the Array using strict equality (the same method used by the ===, or triple-equals, operator).

You cannot use === to check the equability of an object.

As @RobG pointed out

> Note that by definition, two objects are never equal, even if they have exactly the same property names and values. objectA === objectB if and only if objectA and objectB reference the same object.

You can simply write a custom indexOf function to check the object.

function myIndexOf(o) {    
    for (var i = 0; i < arr.length; i++) {
        if (arr[i].x == o.x && arr[i].y == o.y) {
            return i;
        }
    }
    return -1;
}

DEMO: http://jsfiddle.net/zQtML/

Solution 2 - Javascript

As nobody has mentioned built-in function Array.prototype.findIndex(), I'd like to mention that it does exactly what author needs.

> The findIndex() method returns the index of the first element in the > array that satisfies the provided testing function. Otherwise -1 is > returned.

var array1 = [5, 12, 8, 130, 44];

function findFirstLargeNumber(element) {
  return element > 13;
}

console.log(array1.findIndex(findFirstLargeNumber));
// expected output: 3

In your case it would be:

arr.findIndex(function(element) {
 return element.x == 1 && element.y == 2;
});

Or using ES6

arr.findIndex( element => element.x == 1 && element.y == 2 );

More information with the example above: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

Solution 3 - Javascript

As noted, two objects are never equal, but references can be equal if they are to the same object, so to make the code do what you want:

var a = {x:1, y:2};
var b = {x:3, y:4};
var arr = [a, b];

alert(arr.indexOf(a)); // 0
Edit

Here's a more general specialIndexOf function. Note that it expects the values of the objects to be primitives, otherwise it needs to be more rigorous.

function specialIndexOf(arr, value) {
  var a;
  for (var i=0, iLen=arr.length; i<iLen; i++) {
    a = arr[i];
    
    if (a === value) return i;
    
    if (typeof a == 'object') {
      if (compareObj(arr[i], value)) {
        return i;
      }
    } else {
      // deal with other types
    }
  }
  return -1;

  // Extremely simple function, expects the values of all 
  // enumerable properties of both objects to be primitives.
  function compareObj(o1, o2, cease) {
    var p;
  
    if (typeof o1 == 'object' && typeof o2 == 'object') {
  
      for (p in o1) {
        if (o1[p] != o2[p]) return false; 
      }
    
      if (cease !== true) {
        compareObj(o2, o1, true);
      }
  
      return true;
    }
  }
}

var a = new String('fred');
var b = new String('fred');

var arr = [0,1,a];

alert(specialIndexOf(arr, b)); // 2

Solution 4 - Javascript

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

Solution 5 - Javascript

Those objects aren't equal.

You must implement your own function.

You may do that for example :

var index = -1;
arr.forEach(function(v, i) {
   if (this.x==v.x && this.y==v.y) index=i;
}, searched); 

where searched is one of your object (or not).

(I would implement it with a simple loop but it's prettier with foreach)

Solution 6 - Javascript

Because two separate objects are not === to each other, and indexOf uses ===. (They're also not == to each other.)

Example:

var a = {x:1, y:2};
var b = {x:1, y:2};
console.log(a === b);

=== and == test for whether their operands refer to the same object, not if they refer to equivalent objects (objects with the same prototype and properties).

Solution 7 - Javascript

Here's another solution, where you pass a compare function as a parameter :

function indexOf(array, val, from, compare) {

  if (!compare) {
    if (from instanceof Function) {
      compare = from;
      from = 0;
    }
    else return array.__origIndexOf(val, from);
  }

  if (!from) from = 0;

  for (var i=from ; i < array.length ; i++) {
    if (compare(array[i], val))
      return i;
  }
  return -1;
}

// Save original indexOf to keep the original behaviour
Array.prototype.__origIndexOf = Array.prototype.indexOf;

// Redefine the Array.indexOf to support a compare function.
Array.prototype.indexOf = function(val, from, compare) {
  return indexOf(this, val, from, compare);
}

You can then use it these way:

indexOf(arr, {x:1, y:2}, function (a,b) {
 return a.x == b.x && a.y == b.y;
});

arr.indexOf({x:1, y:2}, function (a,b) {
 return a.x == b.x && a.y == b.y;
});

arr.indexOf({x:1, y:2}, 1, function (a,b) {
 return a.x == b.x && a.y == b.y;
});

The good thing is this still calls the original indexOf if no compare function is passed.

[1,2,3,4].indexOf(3);

Solution 8 - Javascript

Looks like you weren't interested in this type of answer, but it is the simplest to make for others who are interested:

var arr = new Array(
    {x:1, y:2},
    {x:3, y:4}
);

arr.map(function(obj) {
    return objStr(obj);
}).indexOf(objStr({x:1, y:2}));

function objStr(obj) {
    return "(" + obj.x + ", " + obj.y + ")"
}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionJiblaView Question on Stackoverflow
Solution 1 - JavascriptSelvakumar ArumugamView Answer on Stackoverflow
Solution 2 - JavascriptMattView Answer on Stackoverflow
Solution 3 - JavascriptRobGView Answer on Stackoverflow
Solution 4 - JavascriptXeltorView Answer on Stackoverflow
Solution 5 - JavascriptDenys SéguretView Answer on Stackoverflow
Solution 6 - JavascriptT.J. CrowderView Answer on Stackoverflow
Solution 7 - JavascriptKristian BenoitView Answer on Stackoverflow
Solution 8 - JavascriptDalView Answer on Stackoverflow