0

I will go straight to code on this one because it is very specific. How can I test if arr contains an element which is itself a specific array. Why can I not use an array literal as an arg to indexOf? I'm trying to see if an array of coordinates contains a specific coordinate pair.

var arr = [[0,0], [1,1]];
arr[0]; // [0, 0]
arr.indexOf([0, 0]); // -1
var y = arr[0];
arr.indexOf(y); // 0
var x = [0, 0];
arr.indexOf(x); // -1
1

6 Answers 6

3

As others have pointed out, [0, 0] !== [0, 0]. Instead, use findIndex with a function which checks array equality:

var match = [0, 0];

array.findIndex(function(elt) { return arrayIsEqual(elt, match); })

Now you just have to write arrayIsEqual. See How to check if two arrays are equal with JavaScript? for some ideas. If your arrays always have two elements, then it could be just elt[0] === match[0] && elt[1] === match[1].


Obligatory disclaimer: findIndex is ES6. However, it's implemented almost everywhere you would care about, except apparently IE11. If need be, use a polyfill or write your own.

Sign up to request clarification or add additional context in comments.

2 Comments

Wow, this is surprisingly difficult. I would have thought this would be a common and solved problem. Well, to follow your method @torazuburo, I suppose the best (most foolproof) implementation of arrayIsEqual is to do an index-by-index comparison of both arrays.
There is another question on SO about why JS doesn't provide native deep-equal capabilities, that I can't find right now, but it basically said that (a) it wasn't necessary because it was pretty easy for users to write and (b) there are many different variations of deep-equality checking.
1

"Why can I not use an array literal as an arg to indexOf?"

I beleive the problem here is that an Array is an object. You cannot create a new object

var x = [0, 0];

and expect it to match arr[0]; because it is not the same object in memory.

To demonstrate I think this would work, but I havent tested it:

var arr = [[0,0], [1,1]];
var x = [0, 0];
arr[0] = x;
arr.indexOf(x); // 0

4 Comments

Sorry, I show that I tried your strategy in my question. It doesn't work.
@RichardWalker Actually, you did not. This is shown and explained in my answer. He is setting the reference of arr[0] to the same reference in this answer... you are not in your code (you have 2 different literals which have different references).
I assigned the x object to arr[0] before looking for the indexOf(x). Its different.
What @Goblinlord said :)
1

We can use instanceOf to check the type of variable or value

if(value instanceOf Array)
{
 //value is of array type
}

and if you want to compare it with some specific array try below code in if statement

var is_same = (array1.length == array2.length) && array1.every(function(element, index) {
    return element === array2[index]; 
});

if is_same is true then array is identical

2 Comments

But I need to know not just that it's an array, but if it is a specific array. These are coordinates. thx –
Good solution to array comparison, but can you show how this would be used in the context of the OP's entire question?
1

Passing objects into the array#indexOf method might not give the results you expect

The indexOf() method returns the first index at which a given element can be found in the array, or -1 if it is not present.

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

Hopefully, someone else can confirm this.

Sounds like the perfect use for array.prototype.some

function hasCoordinates(array, coords) {
    return array.some(function(element, coords) {
        return Array.isArray(element) && String(array[element]) == String(coords);
    });
}

hasCoordinates([1,3,[4,6]], [4,6])
=> should return true

We can use the isArray method to determine if an object is array.

It should be noted that the some function is only available in ie9 and up

To find the exact coordinates, you can not compare arrays for equality, as they are treated as different objects.

Ex.

[0,0] == [0,0]
=> false

We need to perform type conversion first

String[0,0] == String[0,0]
=> true

This is because, now the arrays are being evaluated as strings.

4 Comments

But I need to know not just that it's an array, but if it is a specific array. These are coordinates. thx
No, it is not correct to say that it's impossible to pass objects into the array#indexOf method. You can pass any old value you want.
Reworded it. Does it look better now?
No no no. Do not compare arrays using stringifcation. For instance, this will result in a false match on ['1,', 2] and [1, ',2']. Your solution also does not return the matching index as the OP wants, but rather just a simple true or false.
1

You must use a comparable type to use .indexOf(). When you use a comparison operator with objects (and Arrays are objects) then JS uses reference comparison (MDN Docs). It is probably not what you want to do but you can use a reference as I will show below:

var a = [0, 0];
var b = [1, 1];
var c = [1, 1];
var e = c;  // e now has the same reference as c

console.log(b == c); // false - Reference comparison is used here
console.log(c == e); // true - References are the same
var d = [a, b, c];

console.log(d.indexOf(a)); // 0
console.log(d.indexOf(b)); // 1
console.log(d.indexOf(c)); // 2
console.log(d.indexOf(e)); // 2

If you create 2 objects with the same values inside they still do not have the same reference (like b and c in the above code). As mentioned by @torazaburo you could instead use the .findIndex() function.

You can do this something like below where you pass in your array to find and it returns a function which returns true when it matches each element.

var a = [0, 0],
  b = [0, 0],
  c = [0, 1];

var d = [a, b, c];

function equalArrays(a) {
  return function(b) {
    if (a.length != b.length)
      return false;
    for (var i = 0, len = a.length; i < len; i++) {
      if (a[i] != b[i])
        return false;
    }
    return true;
  }
}

console.log(d.findIndex(equalArrays([0, 0]))); // 0 - index of FIRST array matching [0,0]
console.log(d.findIndex(equalArrays([0, 1]))); // 2 - index of an array matching [0,1]
console.log(d.findIndex(equalArrays([1, 1]))); // -1 - No match found

1 Comment

There were a lot of good other answers. Wanted to show sportsmanship and vote them up
0

To answer the last question "Why can I not use an array literal as an arg to indexOf?" the answer is due to how indexOf is defined.

Refer to the ECMAScript 2015 Array.prototype.indexOf spec: http://www.ecma-international.org/ecma-262/6.0/#sec-array.prototype.indexof

indexOf compares searchElement to the elements of the array, in ascending order, using the Strict Equality Comparison algorithm (7.2.13), and if found at one or more indices, returns the smallest such index; otherwise, −1 is returned.

The important part is the algorithm used; Strict Equality Comparison. In other words, triple equals.

Consider this JS:

var a = [1, 2];
var b = a === [1, 2]; // true or false?

What is the value of b? It's false. If you want to learn more about why that is the case, I recommend reading https://stackoverflow.com/a/14853974/1022333.

2 Comments

It's not because of the ===, == wouldn't work either. It's because it's comparing references, and those are two different references.
I wouldn't say this answer is wrong because == and === behave the same way. In fact, it answers the question about why OP's assumption was false. The linked answer goes into more detail about deep array comparison.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.