Dans un tableau d'objets, moyen le plus rapide de trouver l'index d'un objet dont les attributs correspondent à une recherche

135

J'ai surfé un peu partout pour essayer de trouver un moyen efficace de le faire, mais je n'ai abouti à rien. J'ai un tableau d'objets qui ressemble à ceci:

array[i].id = some number;
array[i].name = some name;

Ce que je veux faire est de trouver les INDEXES des objets où id est égal, par exemple, à 0,1,2,3 ou 4. Je suppose que je pourrais simplement faire quelque chose comme:

var indexes = [];
for(i=0; i<array.length; i++) {
  (array[i].id === 0) ? { indexes[0] = i }
  (array[i].id === 1) ? { indexes[1] = i }
  (array[i].id === 2) ? { indexes[2] = i }
  (array[i].id === 3) ? { indexes[3] = i }
  (array[i].id === 4) ? { indexes[4] = i }
}

Bien que cela fonctionne, cela semble être assez cher et lent (pour ne pas dire moche), surtout si array.length peut être volumineux. Des idées pour embellir un peu cela? J'ai pensé à utiliser array.indexOf d'une manière ou d'une autre, mais je ne vois pas comment forcer la syntaxe. Ce

array.indexOf(this.id === 0);

par exemple, renvoie undefined, comme il se doit. Merci d'avance!

Petrov
la source
1
Si vous avez un ancien tableau, tout ce que vous pouvez faire est d'itérer. C'est ce que sont les tableaux, un groupe d'objets classés par index de tableau.
Dave Newton
2
Venez découvrir ce post aujourd'hui, pour tous les retardataires, il existe une nouvelle méthode de tableau Array.prototype.findIndex()dans ECMAScript 2015. La réponse acceptée était géniale.
Conrad Lo
Je suis fan de la syntaxe ES6 (utilisez des polyfills, si le support sur les navigateurs hérités est nécessaire). ES7 + ES8 vont être du futur
Fr0zenFyr

Réponses:

391

Vous aimeriez peut-être utiliser des fonctions d'ordre supérieur telles que "map". En supposant que vous souhaitiez effectuer une recherche par attribut "champ":

var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);
var objectFound = array[elementPos];
Pablo Francisco Pérez Hidalgo
la source
9
Cette réponse est excellente car elle répond en fait à la question en fournissant l'index :)
contre-être
3
@ZeroAbsolute Votre fonction appliquée (passée à la carte) peut renvoyer une chaîne de hachage qui devrait fournir une clé unique pour chaque combinaison possible donnée par vos critères. Par exemple: function hashf(el) { return String(el.id) + "_" + String(el.name); }. Ceci est juste un indice: de elementPos = array.map(hashf(x)).indexOf(hash({id:3, name:'Pablo'}));toute évidence, la fonction de hachage que je fournis n'est pas valide dans tous les cas car elle '_'pourrait faire partie de vos valeurs, mais ce n'est qu'un exemple rapide que vous pouvez comprendre différentes méthodes de hachage.
Pablo Francisco Pérez Hidalgo
1
Qu'est-ce que cela renvoie s'il n'est pas trouvé? Je suppose -1, juste curieux. Je vais expérimenter.
Nathan
1
@ NathanC.Tresch Il renvoie -1 car c'est la indexOfvaleur de retour quand il ne peut pas localiser une valeur donnée.
Pablo Francisco Pérez Hidalgo
2
Salut tout le monde au lieu d'utiliser deux méthodes, map, indexOfvous pouvez en utiliser une seule appelée findIndex....... Ex:[{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3}) OR [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)
Umair Ahmed
64

Le moyen le plus simple et le plus simple de trouver l'index d'élément dans un tableau.

Syntaxe ES5: [{id:1},{id:2},{id:3},{id:4}].findIndex(function(obj){return obj.id == 3})

Syntaxe ES6: [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)

Umair Ahmed
la source
4
Je pense que c'est la solution la plus élégante. Pour ceux qui s'inquiètent de la rétrocompatibilité, vous pouvez trouver le polyfill findIndexsur developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
mrogers
2
J'obtiens un avertissement dans mon outil de charpie ES6 que l' obj.id == 3opérateur utilisé ici peut provoquer une conversion de type inattendue, alors utilisez obj.id === 3plutôt l' opérateur, qui teste la valeur et le type égaux.
thclark
1
Cette réponse est au moins 3,5 fois plus rapide que la réponse acceptée ci-dessus. L' aide var elementPos = array.map(function(x) {return x.id; }).indexOf(idYourAreLookingFor);qu'il a fallu 0.03500000002532033 millisecondes l' aide [{id:1},{id:2},{id:3},{id:4}].findIndex(obj => obj.id == 3)qu'il a fallu 0.00999999747378752 millisecondes.
Ovidio Reyna
1
CETTE RÉPONSE est la plus EFFICACE car elle n'itère pas l'ensemble du tableau. La réponse sélectionnée mappera le tableau complet, puis findIndex qui est lié à une itération dans tout le tableau une fois
Karun
26

La nouvelle méthode Array .filter () fonctionnerait bien pour cela:

var filteredArray = array.filter(function (element) { 
    return element.id === 0;
});

jQuery peut également le faire avec .grep ()

edit: il convient de mentionner que ces deux fonctions ne font qu'itérer sous le capot, il n'y aura pas de différence de performance notable entre elles et le roulement de votre propre fonction de filtre, mais pourquoi réinventer la roue.

jbabey
la source
+1, j'oublie toujours les fonctions intégrées comme celle-ci sur les objets.
Tejs
59
Cela ne renvoie pas d'index.
Adam Grant
Cela ne répond pas à cette question précise, mais aidez-moi beaucoup! Merci!
rochasdv
Cela ne renvoie pas l'index.
Riche le
10

Si vous vous souciez de la performance, ne pas aller avec trouver ou filtre ou la carte ou l' une des méthodes décrites ci - dessus

Voici un exemple illustrant la méthode la plus rapide. ICI est le lien vers le test réel

Bloc de configuration

var items = []

for(var i = 0; i < 1000; i++) {
    items.push({id: i + 1})
}

var find = 523

Méthode la plus rapide

var index = -1
for(var i = 0; i < items.length; i++) {
    if(items[i].id === find) {
        index = i;
        break;
    }
}

Méthodes plus lentes

items.findIndex(item => item.id === find)

Méthode LA PLUS LENTE

items.map(item => item.id).indexOf(find);
PirateApp
la source
2
Merci d'avoir fourni cette comparaison! Ce qui est super intéressant, c'est la variation des performances, y compris la méthode qui varie le plus rapidement en fonction du navigateur / moteur JavaScript utilisé pour les exécuter.
Iain Collins
1
Je pense que cela devrait être marqué comme une réponse. Cela montre le moyen le plus rapide et le plus lent.
Painkiller le
Dans votre benchmark, le bloc 2 (en utilisant findIndex) est en fait plus rapide pour moi (sur Microsoft Edge Chromium 83.0.474.0)
rezadru
Le bloc 2 est maintenant plus rapide sur Chrome également
Cody Mikol
8
array.forEach(function (elem, i) {  // iterate over all elements of array
    indexes[elem.id] = i;           // take the found id as index for the
});                                 // indexes array and assign i

le résultat est une liste de recherche pour l'identifiant. avec l'identifiant donné, nous obtenons l'index de l'enregistrement.

Nina Scholz
la source
6
var indices = [];
var IDs = [0, 1, 2, 3, 4];

for(var i = 0, len = array.length; i < len; i++) {
    for(var j = 0; j < IDs.length; j++) {
        if(array[i].id == ID) indices.push(i);
    }
}
Elliot Bonneville
la source
6

Puisqu'il n'y a pas de réponse en utilisant un tableau régulier find:

var one = {id: 1, name: 'one'};
var two = {id: 2, name:'two'}
var arr = [one, two] 

var found = arr.find((a) => a.id === 2)

found === two // true

arr.indexOf(found) // 1
énapupe
la source
3

Une nouvelle façon d'utiliser ES6

let picked_element = array.filter(element => element.id === 0);
Silve2611
la source
picked_elementest un tableau dans ce cas ...
Heretic Monkey
3

const index = array.findIndex(item => item.id === 'your-id');

Cela devrait vous donner l'index de l'élément dans le tableau avec id === votre-id

array = [ {id:1}, {id:2} ];

const index = array.findIndex(item => item.id === 2);

console.log(index);

PulpDood
la source
2

Il me semble que vous pourriez créer un itérateur simple avec un rappel pour les tests. Ainsi:

function findElements(array, predicate)
{
    var matchingIndices = [];

    for(var j = 0; j < array.length; j++)
    {
        if(predicate(array[j]))
           matchingIndices.push(j);
    }

    return matchingIndices;
}

Ensuite, vous pouvez invoquer comme ceci:

var someArray = [
     { id: 1, text: "Hello" },
     { id: 2, text: "World" },
     { id: 3, text: "Sup" },
     { id: 4, text: "Dawg" }
  ];

var matchingIndices = findElements(someArray, function(item)
   {
        return item.id % 2 == 0;
   });

// Should have an array of [1, 3] as the indexes that matched
Tejs
la source
2

Adapter la réponse de Tejs pour mongoDB et Robomongo j'ai changé

matchingIndices.push(j);

à

matchingIndices.push(NumberInt(j+1));
user2584621
la source
2

Utilisation de la mapfonction ES6 :

let idToFind = 3;
let index = someArray.map(obj => obj.id).indexOf(idToFind);
JoeTidee
la source
2

Pour résumer toutes les bonnes réponses ci-dessus et en plus de ma réponse concernant trouver tous les index proviennent de certains des commentaires.

  1. Pour renvoyer l'index de la première occurrence.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }];
const idYourAreLookingFor = 2;

//ES5 
//Output: 1
array.map(function (x) { return x.id; }).indexOf(idYourAreLookingFor);

//ES6 
//Output: 1
array.findIndex(obj => obj.id === idYourAreLookingFor);

  1. Pour renvoyer le tableau d'index de toutes les occurrences, utilisez la méthode Reduce.

const array = [{ id: 1 }, { id: 2 }, { id: 3 }, { id: 4 }, { id: 2 }]
const idYourAreLookingFor = 2;

//ES5
//Output: [1, 4]
array.reduce(function (acc, obj, i) {
  if (obj.id === idYourAreLookingFor)
    acc.push(i);
  return acc;
}, []);

//ES6
//Output: [1, 4]
array.reduce((acc, obj, i) => (obj.id === idYourAreLookingFor) ? acc.concat(i) : acc, [])

trungk18
la source
0

Comme je ne peux pas encore commenter, je souhaite afficher la solution que j'ai utilisée en fonction de la méthode publiée par Umair Ahmed, mais lorsque vous souhaitez rechercher une clé au lieu d'une valeur:

[{"a":true}, {"f":true}, {"g":false}]
.findIndex(function(element){return Object.keys(element)[0] == "g"});

Je comprends qu'il ne répond pas à la question élargie, mais le titre ne spécifie pas ce que l'on attendait de chaque objet, donc je veux partager humblement ceci pour éviter des maux de tête aux autres à l'avenir, alors que je ne démarre pas, ce n'est peut-être pas le solution la plus rapide.

Alex N
la source
0

J'ai créé un petit utilitaire appelé super-array où vous pouvez accéder aux éléments d'un tableau par un identifiant unique avec une complexité O (1). Exemple:

const SuperArray = require('super-array');

const myArray = new SuperArray([
  {id: 'ab1', name: 'John'},
  {id: 'ab2', name: 'Peter'},
]);

console.log(myArray.get('ab1')); // {id: 'ab1', name: 'John'}
console.log(myArray.get('ab2')); // {id: 'ab2', name: 'Peter'}
patotome
la source
Vous voudrez peut-être lire Comment offrir des bibliothèques open-source personnelles? avant de poster ça partout.
Martijn Pieters
@MartijnPieters Je ne l'ai posté que sur quelques questions pertinentes et le projet est gratuit au MIT, alors quel est le problème? Peut-être que vous pourriez être un peu plus tolérant.
patotoma
0
var test = [
  {id:1, test: 1},
  {id:2, test: 2},
  {id:2, test: 2}
];

var result = test.findIndex(findIndex, '2');

console.log(result);

function findIndex(object) {
  return object.id == this;
}

retournera l'indice 1 (fonctionne uniquement dans ES 2016)

extrême
la source
0

J'aime cette méthode car elle est facile à comparer à n'importe quelle valeur de l'objet, quelle que soit la profondeur de son imbrication.

 while(i<myArray.length && myArray[i].data.value!==value){
  i++; 
}
// i now hows the index value for the match. 
 console.log("Index ->",i );
Daniel Lefebvre
la source