35

I have a 2d array like this:

var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];

Each index stores an inner array containing the coordinates of some element.

How can I use Array.indexOf() to check if the newly generated set of coordinates is already contained in arr? I want to push into arr if only the coordinate is NOT a duplicate.

Here is my attempt that didn't work:

if (arr.indexOf([x, y]) == -1) {
    arr.push([x, y]);
}

It looks like indexOf() doesn't work for 2d arrays...

Mogsdad
  • 44,709
  • 21
  • 151
  • 275
phoeberesnik
  • 385
  • 1
  • 6
  • 9

12 Answers12

36

You cannot use indexOf to do complicated arrays (unless you serialize it making everything each coordinate into strings), you will need to use a for loop (or while) to search for that coordinate in that array assuming you know the format of the array (in this case it is 2d).

var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];
var coor1 = [0, 9];
var coor2 = [1, 2];

function isItemInArray(array, item) {
    for (var i = 0; i < array.length; i++) {
        // This if statement depends on the format of your array
        if (array[i][0] == item[0] && array[i][1] == item[1]) {
            return true;   // Found it
        }
    }
    return false;   // Not found
}

// Test coor1
console.log("Is it in there? [0, 9]", isItemInArray(arr, coor1));   // True

// Test coor2
console.log("Is it in there? [1, 2]", isItemInArray(arr, coor2));   // False

// Then
if (!isItemInArray(arr, [x, y])) {
   arr.push([x, y]);
}

This implementation loops and grabs every value. If you care about performance you can do more complicated things like sorting the original array by the first index and then using binary search on the first index.

Another way is to bucket the first coordinate of each item in the array in an object (like a hashtable) and bucket the second value in each of those buckets to reduce search times; more info here http://en.wikipedia.org/wiki/Bucket_sort.

Otherwise this is probably good enough for what you need.

user654628
  • 1,429
  • 1
  • 17
  • 37
  • Was OP's question not to return the index of that element? Should you not be returning a number instead of a boolean? `return true` => `return i` – Joe Moore Sep 07 '22 at 10:48
7

Working js fiddle

for(var k = 0; k < arr.length; k++){
    if(arr[k][0] == x && arr[k][1] == y){
        found = true;
    }
}

Much more of a hacky way than a simple index of, but it works

joegandy
  • 320
  • 1
  • 5
3

Not a complete answer just a side note that may help.

Use Lodash

This method will get you the position of a value within a 2 dimensional array

let a = [ [ 'bird' ], [ 'cat' ], [ 'dog' ], [ 'cow' ], [ 'bird' ] ];
let b = _.findIndex(a, function(el) { return el[0] == 'cow'; });
console.log(b);//answer is 3

As mentioned earlier you need a nested loop for traversing through the array.

Mendo
  • 319
  • 2
  • 7
2

Very simple without indexOf...

var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];
const isDup = (x,y) => {
   arr.find(it => JSON.stringify(it) == JSON.stringify([x,y])) == undefined ? arr.push([x,y]) : null
}

console.log(isDup(2,3)) /* Does not add */
console.log(isDup(1,2)) /*Does add*/
console.log(arr) /*Confirmation*/
caroham29
  • 19
  • 3
  • 1
    I did want to have an index so I used: `const find = this.pointList.findIndex(it => JSON.stringify(it) === JSON.stringify(item));` – Jop Knoppers Sep 12 '19 at 09:15
1

Because this is a two dimensional Array, you will need a nested for loop.

var newArr = [1, 2],
    counter;


for ( var i = 0; i < arr.length; i++ ) {

    for ( var x = 0; x = arr[i].length; x++ ) {

        if ( arr[i][x] === newArr[x] {

             counter++ 
        }

        if (counter === 2) {
            alert('new coord!')
        }
    }
    //reset counter
    counter = 0;
}
Jenny Vallon
  • 3
  • 1
  • 4
bencripps
  • 2,025
  • 3
  • 19
  • 27
1

Here is a solution done using prototype so the usage resembles that of indexOf but for 2d arrays. Use in the same way: arr.indexOf2d([2,3]);

var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];

Array.prototype.indexOf2d = function(item) {
    // arrCoords is an array with previous coordinates converted to strings in format "x|y"
    arrCoords = JSON.stringify(this.map(function(a){return a[0] + "|" + a[1]}));

    // now use indexOf to find item converted to a string in format "x|y"
    return arrCoords.indexOf(item[0] + "|" + item[1]) !== -1;
}
arr.indexOf2d([2,3]); // true
arr.indexOf2d([1,1]); // true
arr.indexOf2d([6,1]); // false
Steve Lage
  • 692
  • 6
  • 11
1

Here's what I implemented

getIndexOfArray(array: any[], findArray: any[]): number{
  let index = -1;
  array.some((item, i)=>{
    if(JSON.stringify(item) === JSON.stringify(findArray)) {
      index = i;
      return true;
    }
  });
  return index;
}

here array is the array in which we need the index and findArray is the array whose index will be returned.
Note: This function will return only the first occurrence of findArray array insight array array.

Abhijit
  • 382
  • 3
  • 10
0

A previous answer said:

You cannot use indexOf to do complicated arrays (unless you serialize it making everything each coordinate into strings)...

Here's how you'd do just that. If you have an extremely large data set, I'd recommend against this technique, as it relies on a duplicate of your 2D array. But for reasonable sets, it's simple.

Use a consistent method for flattening array elements, such as:

// Flatten array into a string, separating elements with a "unique" separator.
function stringle( arr ) {
  return arr.join(' |-| ');
}

This is overkill for your example, where sub-arrays contain integers, but it accounts for more complex data types. (If we used a comma, the default, it would be indiscernible from a string element that contained a comma, for example.)

Then the target array can be flattened into an array of strings:

// Transmogrify arr into a String[], usable with indexOf()
var arrSearch = arr.map(function(row) { return stringle(row); });

Then you can use Array.indexOf() (or other array methods) to check for the presence or location of matches.

if (arrSearch.indexOf( stringle(newArray) ) === -1) ...

This snippet contains a demo of this, with multiple data types.

// Example starting array
var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];

// Flatten array into a string, separating elements with a "unique" separator.
function stringle( arr ) {
  return arr.join(' |-| ');
}

snippet.log("arr: "+JSON.stringify(arr));

// Transmogrify arr into a String[], usable with indexOf()
var arrSearch = arr.map(function(row) { return stringle(row); });

snippet.log("arrSearch: "+JSON.stringify(arrSearch));

var tests = [[0, 9],[1, 2],["pig","cow"],[0,9,"unicorn"],["pig","cow"]];

for (var test in tests) {
  var str = stringle(tests[test]);
  if (arrSearch.indexOf(str) === -1) {
    arr.push(tests[test]);
    arrSearch.push(str);
    snippet.log("Added "+JSON.stringify(tests[test]));
  }
  else {
    snippet.log("Already had "+JSON.stringify(tests[test]));
  }
}

snippet.log("Result: "+JSON.stringify(arr));
<!-- Provides the `snippet` object, see http://meta.stackexchange.com/a/242144/134069 -->
<script src="http://tjcrowder.github.io/simple-snippets-console/snippet.js"></script>
Mogsdad
  • 44,709
  • 21
  • 151
  • 275
0

you can use this method,

function isArrayItemExists(array , item) {
    for ( var i = 0; i < array.length; i++ ) {
        if(JSON.stringify(array[i]) == JSON.stringify(item)){
            return true;
        }
            }
            return false;
}
Dinu
  • 123
  • 1
  • 2
  • 7
0

ES2020 Update

If you are able to support ES2020, you can use the new ?. operator (optional chaining operator). It can be used for 2D arrays as follows:

const arr = [[1,2],[3,4]];

if ( ! arr[5]?.[6] ) {
  console.log("Index out of bounds");
}

if ( arr[0]?.[0] ) {
  console.log("Index in bounds");
}

If you try to access the property of anything undefined using the optional chaining operator, it will evaluate to undefined instead of throwing something like Cannot read property 'foo' of 'undefined'.


Documentation

applemonkey496
  • 683
  • 1
  • 10
  • 29
0

Array method to find the index of an element in 2D arrays:

To get the index of an element from a 2d array, you can loop over the first layer of the array and use indexOf() in the second layer. I made an Array prototype like indexOf, but for 2D arrays and without its second argument (IndexOf(_, fromIndex)):

Array.prototype.twoDIndexOf = function(element){
  if (this === null || this === undefined)
    throw TypeError("Array.prototype.indexOf called on null or undefined")
  for(let i = 0; i < this.length; i++){
    const curr = this[i]
    if(curr.includes(element))
      return [i, curr.indexOf(element)];
  }
  return -1;
}


const exArray =  [
  ['1', '2', '3'],
  ['4', '5', '6'],
  ['7', '8', '9'],
  ['', '0', ''],
]

console.log(exArray.twoDIndexOf('7')); // [2, 0]
console.log(exArray.twoDIndexOf('6')); // [1, 2]
console.log(exArray.twoDIndexOf('')); // [3, 0]
console.log(exArray.twoDIndexOf('x')); // -1

Note that the method returns the first index of the element, which means if the element is duplicated many times in the same array, the value returned is its first position.

othwsav
  • 23
  • 6
0

You can use this one-liner "findIndexIn2dArray" function:

var arr = [[2,3],[5,8],[1,1],[0,9],[5,7]];
var pos = [0,9];


const findIndexIn2dArray = (array, search) => array.findIndex((n) => n.every((e, i) => search[i] !== undefined && search[i] === e));

const x = findIndexIn2dArray(arr, pos);

console.log(x);

Here's how the function works:

The findIndex method is called on the array parameter. This method searches the array for the first subarray that satisfies the condition specified in the callback function that it receives.

The callback function passed to findIndex takes in a parameter n, which represents each subarray in the array.

For each subarray n, the every method is called. The every method checks whether every element in the subarray satisfies a condition specified in the callback function that it receives.

The callback function passed to every takes in two parameters: e, which represents each element in the subarray, and i, which represents the index of the element in the subarray.

The callback function checks whether the i-th element in the search array is not undefined and whether it is equal to the i-th element in the n subarray. If both conditions are true for every element in the n subarray, then the every method returns true.

If the every method returns true for a subarray n, then the findIndex method returns the index of that subarray in the array. If no subarray in the array matches the search criteria, then the findIndex method returns -1.

Overall, this function can be used to find the index of a specific subarray in a two-dimensional array based on the contents of that subarray.

Brend
  • 194
  • 6