Why Array.indexOf doesn't find identical looking objects
Solution 1
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
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
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
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
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).
Jibla
Updated on July 09, 2022Comments
-
Jibla almost 2 years
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...
-
Jibla over 11 yearsThis means, I cant compare whether two objects are identical or not?
-
jbabey over 11 yearsYou can use
===
to check equality of objects, just expect it to fail when those two objects are completely different literals. -
Selvakumar Arumugam over 11 years@Jibla Write a simple function to iterate over the object and find the match.. Check updated post.
-
Jibla over 11 years@Vega - Yes, I know the least option to write custom function, I just didn't want to write custom one. P.S. Thank you!
-
RobG over 11 years
-
Jibla over 11 yearsyes, but I generate dynamically the object which I search. Thanks.
-
RobG over 11 yearsThen you will have to iterate over the properties and compare values. You also have to compare both ways, i.e. a to be and b to a.
-
RobG over 11 years@vega—that is not a general solution, it is specific to the OP.
-
Jibla over 11 yearsWhy both ways? I wrote function like this: availableMoves.indexOf = function(obj) { for(i in this) { if (this[i].x == obj.x && this[i].y == obj.y) { return parseInt(i); } } return -1; }
-
Selvakumar Arumugam over 11 years@RobG Is it supposed to be specific to OP? We can do a
for..in
comparision.. but it would be a overhead here. -
RobG over 11 yearsThat will work if you have exactly those property names. Incidentally, using for..in over an array is not recommended, particularly with the popularity of "monkey patches" for
Array.prototpye
to add ES5 features that add enumerable properties. Use a normalfor
loop with a numeric index, then you can just returni
and don't need to convert it to a number. -
Brandon Clark over 9 yearsNice method for when jQuery
inArray()
is not available. I needed this function for sub-object search which took a slight modification. -
Vishal Kumar Sahu over 5 yearsCongratulations, you are still faster than some libraries.
-
JesseBoyd over 3 yearsbest answer and requires writing no new functions!