méthode indexOf dans un tableau d'objets?


514

Quelle est la meilleure méthode pour obtenir l'index d'un tableau contenant des objets?

Imaginez ce scénario:

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

Maintenant, je voudrais avoir l' indexOfobjet dont la hellopropriété est 'stevie'qui, dans cet exemple, serait 1.

Je suis assez novice avec JavaScript et je ne sais pas s'il existe une méthode simple ou si je dois créer ma propre fonction pour le faire.


1
Voulez-vous fusionner les deux objets helloet qaz?
Armin

Non, je ne sais pas. Je veux avoir une liste d'objets dans un tableau.
Antonio Laguna

Ah ok! Vous voulez connaître la position de l'objet entier dans le tableau, qui a une propriété définie.
Armin

10
J'ai trouvé une fonction très simple pour résoudre ce problème exact avec cette réponse SO: var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor); var objectFound = array[elementPos]; [link] ( stackoverflow.com/a/16100446/1937255 )
Rick

ES6 Array.indexOf est meilleure que la réponse acceptée (si ES6 fonctionne pour vous) - voir l'exemple complet ci
yar1

Réponses:


1045

Je pense que vous pouvez le résoudre en une seule ligne en utilisant la fonction de carte :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

58
Cela devrait être honnêtement la réponse acceptée. La plupart des navigateurs supportent aujourd'huiArray.prototype.map()
AlbertEngelB

10
Il n'est pas pris en charge par IE8 mais, si ce n'est pas un problème, c'est la meilleure solution.
Antonio Laguna

57
Um ... ne vaut-il pas la peine de noter que Array.prototype.map () crée un tout nouveau tableau contenant les éléments mappés? Donc, si vous avez un tableau avec 1000 éléments, vous avez d'abord créé un autre tableau avec 1000 éléments, puis recherchez-le? Il vaudrait la peine, je pense, de voir les performances de cette méthode par rapport à une simple boucle for. Surtout lorsque vous utilisez une plate-forme mobile avec des ressources limitées.
Doug

7
@Doug Bien que votre point de vue sur les performances soit en effet correct, qui, dans son bon sens, remplacerait une ligne de code par sept pour une application qui est presque par définition liée aux E / S jusqu'à ce qu'ils aient profilé les goulots d'étranglement?
Jared Smith

6
Techniquement, un fichier js minifié est également une ligne; D
supérieurKing

371

Array.prototype.findIndex est pris en charge dans tous les navigateurs autres qu'IE (non-edge). Mais le polyfill fourni est agréable.

var indexOfStevie = myArray.findIndex(i => i.hello === "stevie");

La solution avec la carte est correcte. Mais vous parcourez l'ensemble du tableau à chaque recherche. Ce n'est que le pire des cas pour findIndex qui cesse d'itérer une fois qu'une correspondance est trouvée.


Il n'y a pas vraiment de manière concise (lorsque les développeurs devaient se soucier d'IE8) , mais voici une solution courante:

var searchTerm = "stevie",
    index = -1;
for(var i = 0, len = myArray.length; i < len; i++) {
    if (myArray[i].hello === searchTerm) {
        index = i;
        break;
    }
}

ou en fonction:

function arrayObjectIndexOf(myArray, searchTerm, property) {
    for(var i = 0, len = myArray.length; i < len; i++) {
        if (myArray[i][property] === searchTerm) return i;
    }
    return -1;
}
arrayObjectIndexOf(arr, "stevie", "hello"); // 1

Juste quelques notes:

  1. Ne pas utiliser pour ... dans les boucles sur les tableaux
  2. Assurez-vous de sortir de la boucle ou de revenir hors de la fonction une fois que vous avez trouvé votre "aiguille"
  3. Soyez prudent avec l'égalité des objets

Par exemple,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found

la fonction a la comparaison searchterm incorrecte comme elle devrait l'être searchTerm :)
Antonio Laguna

il y a eu plusieurs événements
Joe

3
@SteveBennett c'est une version optimisée pour les performances; la longueur du tableau ne doit être déterminée qu'une seule fois (lorsque les variables de la boucle for sont initialisées). Dans votre cas, la longueur est vérifiée à chaque itération. Voir aussi stackoverflow.com/questions/5349425/… et stackoverflow.com/questions/8452317/… Cependant, si les performances ne sont pas élevées, cela n'a pas vraiment d'importance.
loother

1
Bonne réponse, mais j'ai fait une analyse comparative des performances (voir jsperf.com/find-index-of-object-in-array-by-contents ), et j'ai constaté que la réponse basée sur les fonctions mentionnée ici semble être la deuxième réponse la plus performante. La seule chose plus performante finit par le mettre dans un prototype, au lieu d'une simple fonction, comme mentionné dans ma réponse .
Uniphonic

123

Dans ES2015, c'est assez simple:

myArray.map(x => x.hello).indexOf('stevie')

ou, probablement avec de meilleures performances pour les baies plus grandes:

myArray.findIndex(x => x.hello === 'stevie')

2
Bonne approche pour utiliser ES6
kag

J'ai été surpris qu'aucune de ces méthodes ne soit aussi performante que le prototype de boucle, comme mentionné dans ma réponse. Même si la prise en charge du navigateur de la méthode findIndex est un peu médiocre, il semble que ce serait la même chose, mais finit toujours moins performant? Voir le lien dans ma réponse pour les repères.
Uniphonic

Bon à savoir si les performances sont importantes pour la tâche à accomplir. C'est très rarement, d'après mon expérience, mais ymmv.
Steve Bennett

24
var idx = myArray.reduce( function( cur, val, index ){

    if( val.hello === "stevie" && cur === -1 ) {
        return index;
    }
    return cur;

}, -1 );

17

J'aime la réponse de Pablo, mais Array # indexOf et Array # map ne fonctionnent pas sur tous les navigateurs. Underscore utilisera du code natif s'il est disponible, mais a également des solutions de rechange. De plus, il a la méthode pluck pour faire exactement ce que fait la méthode de carte anonyme de Pablo.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();

1
Array.prototype.map () est compatible avec IE9 + et vous pouvez utiliser un Polyfill pour IE8, 7, 6: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria

1
Vous pouvez utiliser un polyfill. . . ou vous pouvez simplement utiliser un trait de soulignement ou un lodash, qui sont essentiellement des polyfills qui ont tout un tas d'autres goodies attachés . Quelle est l'objection avec le soulignement? Taille?
tandrewnichols

J'aime vraiment Underscore, votre réponse est intelligente aussi, mais la réponse de IMHO Pablo est la plus propre.
Johann Echavarria

Wow, je n'ai jamais pensé à utiliser le chaînage comme ça. J'aime vraiment comment cela rend la recherche fluide.
Dylan Pierce

chainest superflu ici. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett

14

Ou prototype:

Array.prototype.indexOfObject = function arrayObjectIndexOf(property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArr.indexOfObject("name", "stevie");

9
Très pratique! Bien que je choisisse prototype.indexOfObject afin de ne pas interférer avec la méthode Array.indexOf existante. Array.prototype.indexOfObject = function(property, value) { for (var i = 0, len = this.length; i < len; i++) { if (this[i][property] === value) return i; } return -1; };
Adam

1
Je l'envelopperais dans une fermeture auto-exécutable avec l'ancien étant stocké à l'avance, la première ligne de la fonction de remplacement étant quelque chose dans le sens de if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. C'est parce que ce sont les quelques types qui sont immuables. Je présenterais également un troisième argument pour permettre le retour à la méthode native si nécessaire.
Isiah Meadows

8

Bref

myArray.indexOf('stevie','hello')

Cas d'utilisation:

  /*****NORMAL****/  
[2,4,5].indexOf(4) ;//OUTPUT 1
 /****COMPLEX*****/
 [{slm:2},{slm:4},{slm:5}].indexOf(4,'slm');//OUTPUT 1
 //OR
 [{slm:2},{slm:4},{slm:5}].indexOf(4,function(e,i){
   return e.slm;
});//OUTPUT 1
/***MORE Complex**/
[{slm:{salat:2}},{slm:{salat:4}},{slm:{salat:5}}].indexOf(4,function(e,i){
   return e.slm.salat;
});//OUTPUT 1

API:

    Array.prototype.indexOfOld=Array.prototype.indexOf

    Array.prototype.indexOf=function(e,fn){
      if(!fn){return this.indexOfOld(e)}
      else{ 
       if(typeof fn ==='string'){var att=fn;fn=function(e){return e[att];}}
        return this.map(fn).indexOfOld(e);
      }
    };

6

J'ai fait quelques tests de performances de différentes réponses ici, que tout le monde peut exécuter eux-mêmes:

https://jsperf.com/find-index-of-object-in-array-by-contents

D'après mes premiers tests dans Chrome, la méthode suivante (en utilisant une boucle for configurée dans un prototype) est la plus rapide:

Array.prototype.indexOfObject = function (property, value) {
    for (var i = 0, len = this.length; i < len; i++) {
        if (this[i][property] === value) return i;
    }
    return -1;
}

myArray.indexOfObject("hello", "stevie");

Ce code est une version légèrement modifiée de la réponse de Nathan Zaetta.

Dans les tests de performances, je l'ai essayé avec à la fois la cible au milieu (index 500) et à la fin (index 999) d'un tableau d'objets 1000, et même si je mets la cible en tant que tout dernier élément du tableau (ce qui signifie qu'il doit parcourir chaque élément du tableau avant qu'il ne soit trouvé), il finit toujours par être le plus rapide.

Cette solution a également l'avantage d'être l'une des plus laconiques pour une exécution répétée, car seule la dernière ligne doit être répétée:

myArray.indexOfObject("hello", "stevie");

2
J'étais sur le point de poster une réponse à cette question en utilisant un violon avec mes propres tests, mais grâce à votre réponse, je n'ai plus besoin de le faire. Je veux juste confirmer vos tests - je suis arrivé au même résultat, mais en utilisant une whileboucle au lieu d'une for, et performance.now(). J'aurais aimé que cette réponse soit plus votée et que je l'ai vue plus tôt, cela m'aurait fait gagner du temps ...
Yin Cognyto


5

Bien que la plupart des autres réponses ici soient valides. Parfois, il est préférable de créer une fonction simple et courte près de l'endroit où vous allez l'utiliser.

// indexOf wrapper for the list of objects
function indexOfbyKey(obj_list, key, value) {
    for (index in obj_list) {
        if (obj_list[index][key] === value) return index;
    }
    return -1;
}
// Find the string in the list (default -1)
var test1 = indexOfbyKey(object_list, 'name', 'Stevie');
var test2 = indexOfbyKey(object_list, 'last_name', 'some other name');

Cela dépend de ce qui est important pour vous. Cela pourrait économiser des lignes de code et être très intelligent pour utiliser une seule ligne, ou pour mettre une solution générique quelque part qui couvre divers cas de bord. Mais parfois, il vaut mieux simplement dire: «ici, je l'ai fait comme ça» plutôt que de laisser les futurs développeurs faire un travail d'ingénierie inverse supplémentaire. Surtout si vous vous considérez comme un "débutant" comme dans votre question.


4
array.filter(function(item, indx, arr){ return(item.hello === 'stevie'); })[0];

Attention au [0].

Il convient d'utiliser reducecomme dans Antonio Lagunala réponse de.

Toutes mes excuses pour la brièveté ...


4

Si votre objet est le même objet que ceux que vous utilisez dans le tableau, vous devriez pouvoir obtenir l'index de l'objet de la même manière que si c'était une chaîne.

var hello = {
    hello: 'world',
    foo: 'bar'
};
var qaz = {
    hello: 'stevie',
    foo: 'baz'
}

var qazCLONE = { // new object instance and same structure
    hello: 'stevie',
    foo: 'baz'
}

var myArray = [hello,qaz];

myArray.indexOf(qaz) // should return 1
myArray.indexOf(qazCLONE) // should return -1

C'est la réponse que je cherchais car il n'était pas clair pour moi si IndexOf correspondait par valeur ou quoi. Maintenant, je sais que je peux utiliser IndexOf pour trouver mon objet, et ne vous inquiétez pas s'il y a d'autres objets avec les mêmes propriétés.
MDave


3

Facile:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])

3

Si vous souhaitez uniquement trouver le poste, consultez la réponse de @ Pablo .

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');

Cependant, si vous avez hâte de trouver l'élément (c'est-à-dire si vous envisagez de faire quelque chose comme ça myArray[pos]), il existe un moyen plus efficace de le faire sur une seule ligne , en utilisant filter.

element = myArray.filter((e) => e.hello === 'stevie')[0];

Voir les résultats de performance (~ + 42% ops / sec): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3


2

Voir cet exemple: http://jsfiddle.net/89C54/

for (i = 0; i < myArray.length; i++) {
    if (myArray[i].hello === 'stevie') {
        alert('position: ' + i);
        return;
    }
}

Il commence à compter avec zéro.


2

J'ai créé une fonction générique pour vérifier le code ci-dessous et fonctionne pour n'importe quel objet

function indexOfExt(list, item) {
    var len = list.length;

    for (var i = 0; i < len; i++) {
        var keys = Object.keys(list[i]);
        var flg = true;
        for (var j = 0; j < keys.length; j++) {
            var value = list[i][keys[j]];
            if (item[keys[j]] !== value) {
                flg = false;
            }
        }
        if (flg == true) {
            return i;
        }
    }
    return -1;
}

var items = [{ "hello": 'world', "foo": 'bar' }];
var selectedItem = { "hello": 'world', "foo": 'bar' };
alert(items.indexOf(selectedItem));
alert(indexOfExt(items, selectedItem));

La première alerte renverra -1 (signifie que la correspondance n'est pas trouvée) et la deuxième alerte renverra 0 (signifie que la correspondance a été trouvée).


2

Utilisation à _.findIndexpartir de la bibliothèque underscore.js

Voici l'exemple _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1


Si vous suggérez des méthodes à partir de bibliothèques supplémentaires, vous devez mentionner d'où elles viennent.
Craicerjack

@Steve Bennett nice use.of the library now its its in lodash
zabusa

2

En utilisant la findIndexméthode ES6 , sans lodash ni aucune autre bibliothèque, vous pouvez écrire:

function deepIndexOf(arr, obj) {
  return arr.findIndex(function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

Cela comparera les propriétés immédiates de l'objet, mais ne récursif dans les propriétés.

Si votre implémentation ne fournit pas findIndexencore (la plupart ne le font pas), vous pouvez ajouter un polyfill léger qui prend en charge cette recherche:

function deepIndexOf(arr, obj) {
  function findIndex = Array.prototype.findIndex || function (pred) {
    for (let i = 0; i < this.length; ++i) {
      if (pred.call(this, this[i], i)) {
        return i;
      }
    }

    return -1;
  }

  return findIndex.call(arr, function (cur) {
    return Object.keys(obj).every(function (key) {
      return obj[key] === cur[key];
    });
  });
}

(d'après ma réponse sur ce dupe )


2

Vous pouvez utiliser une fonction native et pratique Array.prototype.findIndex()essentiellement:

La méthode findIndex () renvoie un index dans le tableau, si un élément du tableau satisfait la fonction de test fournie. Sinon, -1 est renvoyé.

Juste une note, il n'est pas pris en charge sur Internet Explorer, Opera et Safari, mais vous pouvez utiliser un Polyfill fourni dans le lien ci-dessous.

Plus d'information:

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

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello, qaz);

var index = myArray.findIndex(function(element, index, array) {
  if (element.hello === 'stevie') {
    return true;
  }
});
alert('stevie is at index: ' + index);


2

En plus de la réponse de @Monika Garg , vous pouvez utiliser findIndex()(Il existe un polyfill pour les navigateurs non pris en charge).

J'ai vu que les gens ont rejeté cette réponse, et j'espère qu'ils l'ont fait à cause d'une mauvaise syntaxe, car à mon avis, c'est la manière la plus élégante.

La méthode findIndex () renvoie un index dans le tableau, si un élément du tableau satisfait la fonction de test fournie. Sinon, -1 est renvoyé.

Par exemple:

var hello = {
  hello: 'world',
  foo: 'bar'
};
var qaz = {
  hello: 'stevie',
  foo: 'baz'
}

var myArray = [];
myArray.push(hello,qaz);

var index = myArray.findIndex(function(element) {
  return element.hello == 'stevie';
});

alert(index);


La méthode semble élégante, mais pas de support IE selon MDN? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu

Essayez d'utiliser le polyfill (lien dans la réponse).
Mosh Feu

1

C'est la façon de trouver l'index de l'objet dans le tableau

    var myArray = [{  hello: 'world',
        foo: 'bar'
    },{
        hello: 'stevie',
        foo: 'baz'
    }];



    for (i = 0; i < myArray.length; i++) {
        if (myArray[i].hello === 'stevie') {
            alert('position: ' + i);
            return;
        }
    }

0

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, il indique seulement si votre objet existe dans la structure de données actuelle


1
Ceci n'est pas valide car cela ne permet pas d'obtenir d'index
Antonio Laguna

0

Vous pouvez simplement utiliser

const someId = 2;
const array = [{id:1}, {id:2}, {id:3}];
const index = array.reduce((i, item, index) => item.id === someId ? index : i, -1);
alert('someId ' + someId + ' is at index ' + index);

Pas de soulignement, pas de, juste une réduction.


0
var hello = {hello: "world",  foo: "bar"};
var qaz = {hello: "stevie", foo: "baz"};
var myArray = [];
myArray.push(hello,qaz);

function indexOfObject( arr, key, value   ) {
    var j = -1;
    var result = arr.some(function(obj, i) { 
        j++;
        return obj[key] == value;
    })

    if (!result) {
        return -1;
    } else {
        return j;
    };
}

alert(indexOfObject(myArray,"hello","world"));

Utilisez une méthode Array.
user3235365


-2

Je préférerai utiliser la findIndex()méthode:

 var index = myArray.findIndex('hello','stevie');

index vous donnera le numéro d'index.


1
Réponse, orthographe et indentation de code et :) sont faux?
Sachin Verma

1
findIndex n'est dans aucune implémentation standard javascript. Il y a une proposition à venir (ecma 6) pour une telle méthode, mais sa signature n'est pas comme ça. Veuillez clarifier ce que vous voulez dire (peut-être le nom de la méthode), donner la déclaration de la méthode findIndex ou nommer la bibliothèque que vous utilisez.
Sebastien F.
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.