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
Richard Walker
  • 187
  • 1
  • 2
  • 11
  • Possible duplicate of [How to compare arrays in JavaScript?](http://stackoverflow.com/questions/7837456/how-to-compare-arrays-in-javascript) – rrk Feb 16 '16 at 06:28

6 Answers6

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.

Community
  • 1
  • 1
  • 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. – Richard Walker Feb 16 '16 at 06:45
  • 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. –  Feb 16 '16 at 13:36
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.

Richard Hamilton
  • 25,478
  • 10
  • 60
  • 87
  • But I need to know not just that it's an array, but if it is a specific array. These are coordinates. thx – Richard Walker Feb 16 '16 at 06:19
  • 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. –  Feb 16 '16 at 06:28
  • Reworded it. Does it look better now? – Richard Hamilton Feb 16 '16 at 06:30
  • 1
    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. –  Feb 16 '16 at 06:38
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

Harsh shah
  • 46
  • 2
  • But I need to know not just that it's an array, but if it is a specific array. These are coordinates. thx – – Richard Walker Feb 16 '16 at 06:22
  • Good solution to array comparison, but can you show how this would be used in the context of the OP's entire question? –  Feb 16 '16 at 06:41
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
spryce
  • 616
  • 7
  • 14
  • Sorry, I show that I tried your strategy in my question. It doesn't work. – Richard Walker Feb 16 '16 at 06:25
  • @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). – Goblinlord Feb 16 '16 at 06:32
  • I assigned the x object to arr[0] before looking for the indexOf(x). Its different. – spryce Feb 16 '16 at 06:32
  • What @Goblinlord said :) – spryce Feb 16 '16 at 06:33
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
Goblinlord
  • 3,290
  • 1
  • 20
  • 24
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.

Community
  • 1
  • 1
Rick Viscomi
  • 8,180
  • 4
  • 35
  • 50
  • 1
    It's not because of the ===, == wouldn't work either. It's because it's comparing references, and those are two different references. – aquinas Feb 16 '16 at 06:27
  • 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. – Rick Viscomi Feb 16 '16 at 07:38