web-dev-qa-db-fra.com

Pourquoi Array.indexOf ne trouve pas d'objets identiques

J'ai tableau avec des objets.

Quelque chose comme ça:

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

Quand j'essaye:

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

Il retourne -1.

Si j'ai des chaînes, des nombres ou un autre type d'éléments mais d'objet, alors indexOf() fonctionne correctement.

Quelqu'un sait-il pourquoi et que dois-je faire pour rechercher des éléments d'objet dans un tableau?

Bien sûr, je parle des manières sauf de faire des clés de hachage de chaîne pour les objets et de les donner à array ...

40
Jibla

indexOf compare searchElement aux éléments du tableau en utilisant une égalité stricte (la même méthode est utilisée par l'opérateur ===, ou triple égal à,).

Vous ne pouvez pas utiliser === pour vérifier l'équabilité d'un objet.

Comme @RobG a souligné

Notez que par définition, deux objets ne sont jamais égaux, même s'ils ont exactement les mêmes noms et valeurs de propriétés. objectA === objectB si et seulement si objectA et objectB font référence au même objet.

Vous pouvez simplement écrire une fonction indexOf personnalisée pour vérifier l'objet.

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/

44
Selvakumar Arumugam

Comme indiqué, deux objets ne sont jamais égaux, mais les références peuvent l'être si elles se rapportent au même objet. Pour que le code fasse ce que vous voulez:

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

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

Modifier

Voici une fonction plus générale specialIndexOf . Notez qu'il s'attend à ce que les valeurs des objets soient des primitives, sinon il doit être plus rigoureux.

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
12
RobG

Cela fonctionne sans code personnalisé

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

Remarque: cela ne donne pas l'index réel, mais seulement si votre objet existe dans la structure de données

9
Xeltor

Ces objets ne sont pas égaux.

Vous devez implémenter votre propre fonction.

Vous pouvez le faire par exemple:

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

searched est l'un de vos objets (ou non).

(Je le mettrais en œuvre avec une simple boucle mais c'est plus joli avec foreach)

3
Denys Séguret

Parce que deux objets distincts ne sont pas === l'un pour l'autre, et indexOf utilise ===. (Ils ne sont pas non plus == l'un à l'autre.)

Exemple:

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

=== et == vérifient si leurs opérandes font référence aux objets même objet, et non s'ils se réfèrent à des objets equivalent (objets avec le même prototype et les mêmes propriétés).

2
T.J. Crowder

Voici une autre solution, où vous passez une fonction de comparaison en tant que paramètre:

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);
}

Vous pouvez ensuite l'utiliser de la manière suivante:

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;
});

La bonne chose est que cela appelle toujours l'index d'origine si aucune fonction de comparaison n'est transmise.

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

On dirait que ce type de réponse ne vous intéresse pas, mais c'est le plus simple à faire pour ceux qui sont intéressés

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 + ")"
}
0
Dal

Comme personne n’a mentionné la fonction intégrée Array.prototype.findIndex (), je voudrais mentionner qu’elle répond exactement aux besoins de l’auteur.

La méthode findIndex () renvoie l'index du premier élément du fichier tableau qui satisfait à la fonction de test fournie. Sinon, -1 est revenu.

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

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

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

Dans votre cas ce serait:

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

Ou en utilisant ES6

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

Plus d'informations avec l'exemple ci-dessus: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/findIndex

0
Matt