Récupère l'index de l'objet dans un tableau, correspondant à une condition

322

J'ai un tableau comme celui-ci:

[{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"},...]

Comment puis-je obtenir l'index de l'objet qui correspond à une condition, sans itérer sur l'ensemble du tableau?

Par exemple, étant donné prop2=="yutu", je veux obtenir l'index 1.

J'ai vu .indexOf()mais je pense que c'est utilisé pour des tableaux simples comme ["a1","a2",...]. J'ai également vérifié $.grep()mais cela renvoie des objets, pas l'index.

ampli
la source

Réponses:

733

Depuis 2016, vous êtes censé utiliser Array.findIndex(une norme ES2015 / ES6) pour cela:

a = [
  {prop1:"abc",prop2:"qwe"},
  {prop1:"bnmb",prop2:"yutu"},
  {prop1:"zxvz",prop2:"qwrq"}];
    
index = a.findIndex(x => x.prop2 ==="yutu");

console.log(index);

Il est pris en charge dans Google Chrome, Firefox et Edge. Pour Internet Explorer, il existe un polyfill sur la page liée.

Note de performance

Les appels de fonction sont chers, donc avec de très grands tableaux, une simple boucle fonctionnera beaucoup mieux que findIndex:

let test = [];

for (let i = 0; i < 1e6; i++)
    test.push({prop: i});


let search = test.length - 1;
let count = 100;

console.time('findIndex/predefined function');
    let fn = obj => obj.prop === search;

    for (let i = 0; i < count; i++)
        test.findIndex(fn);
console.timeEnd('findIndex/predefined function');


console.time('findIndex/dynamic function');
    for (let i = 0; i < count; i++)
        test.findIndex(obj => obj.prop === search);
console.timeEnd('findIndex/dynamic function');


console.time('loop');
    for (let i = 0; i < count; i++) {
        for (let index = 0; index < test.length; index++) {
            if (test[index].prop === search) {
                break;
            }
        }
    }
console.timeEnd('loop');

Comme pour la plupart des optimisations, cela doit être appliqué avec soin et uniquement lorsque cela est réellement nécessaire.

georg
la source
3
Je ne vois pas la nécessité d'un tableau temporaire ici. Utilisez simplement le fait que la fonction itérateur se ferme sur le contexte et utilisez une variable. De plus, la version non jQuery ne fonctionne pas (supposons qu'elle se trouve à l'index 0?). Les deux solutions font plus d'itérations que nécessaire, ce qui n'est pas idéal si le tableau est grand (bien que les chances qu'il soit si grand qu'un humain le remarquerait sont faibles, à moins que les recherches ne se produisent souvent ).
TJ Crowder
@ thg435: Pensez toujours que c'est un peu une machine Rube Goldberg où un simple levier ferait l'affaire. :-) Mais bon, ça marche!
TJ Crowder
4
Pouvez-vous expliquer comment x => x.prop2=="yutu"fonctionne findIndex ()?
Abhay Pai
5
@AbhayPai: c'est la même chose quefunction(x) { return x.prop2=="yutu" }
georg
6
J'aime la suggestion d'utiliser le polyfill. Cependant, le code tel qu'écrit échoue toujours dans IE11 même avec le polyfill en raison de l'utilisation d'une fonction flèche / lambda. Réécrit comme index = a.findIndex(function (x) { return x.prop2 == "yutu" })corrigé le problème afin qu'avec le code polyfill, findIndex fonctionne dans IE11
Rick Glos
26

Comment puis-je obtenir l'index de l'objet qui correspond à une condition (sans itérer le long du tableau)?

Vous ne pouvez pas, quelque chose doit parcourir le tableau (au moins une fois).

Si la condition change beaucoup, vous devrez parcourir et regarder les objets qui s'y trouvent pour voir s'ils correspondent à la condition. Cependant, sur un système avec des fonctionnalités ES5 (ou si vous installez un shim), cette itération peut être effectuée de manière assez concise:

var index;
yourArray.some(function(entry, i) {
    if (entry.prop2 == "yutu") {
        index = i;
        return true;
    }
});

Qui utilise la nouvelle (ish) Array#somefonction , qui fait une boucle à travers les entrées du tableau jusqu'à ce que la fonction que vous lui donnez retourne vrai. La fonction que je lui ai donnée enregistre l'index de l'entrée correspondante, puis revient truepour arrêter l'itération.

Ou bien sûr, utilisez simplement une forboucle. Vos différentes options d'itération sont couvertes dans cette autre réponse .

Mais si vous allez toujours utiliser la même propriété pour cette recherche et si les valeurs de propriété sont uniques, vous pouvez boucler une seule fois et créer un objet pour les mapper:

var prop2map = {};
yourArray.forEach(function(entry) {
    prop2map[entry.prop2] = entry;
});

(Ou, encore une fois, vous pouvez utiliser une forboucle ou l'une de vos autres options .)

Ensuite, si vous avez besoin de trouver l'entrée avec prop2 = "yutu", vous pouvez le faire:

var entry = prop2map["yutu"];

J'appelle cela "l'indexation croisée" du tableau. Naturellement, si vous supprimez ou ajoutez des entrées (ou modifiez leurs prop2valeurs), vous devez également mettre à jour votre objet de mappage.

TJ Crowder
la source
Merci pour l'explication! La solution avec jQuery pointée par a thg435fait ce que je voulais ...
ampli
21

Ce que TJ Crowder a dit, chaque fois aura une sorte d'itération cachée, avec lodash cela devient:

var index = _.findIndex(array, {prop2: 'yutu'})
Aliak
la source
1
alors que vous pouvez simplement parcourir différentes façons d'obtenir l'index, trouver Index est la meilleure solution, même adoptée dans ES6 dans les méthodes de tableau natives
Kelly Milligan
13
var CarId = 23;

//x.VehicleId property to match in the object array
var carIndex = CarsList.map(function (x) { return x.VehicleId; }).indexOf(CarId);

Et pour les numéros de tableau de base, vous pouvez également faire ceci:

var numberList = [100,200,300,400,500];
var index = numberList.indexOf(200); // 1

Vous obtiendrez -1 s'il ne trouve pas de valeur dans le tableau.

David Castro
la source
11
var index;
yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' ? (index = i, true) : false;
});

Itérer sur tous les éléments du tableau. Il retourne soit l'index et true ou false si la condition ne correspond pas.

Important est la valeur de retour explicite de true (ou une valeur dont le résultat booléen est true). L'affectation unique n'est pas suffisante, en raison d'un index possible avec 0 (Boolean (0) === false), ce qui ne résulterait pas en une erreur mais désactiverait la rupture de l'itération.

Éditer

Une version encore plus courte de ce qui précède:

yourArray.some(function (elem, i) {
    return elem.prop2 === 'yutu' && ~(index = i);
});
Nina Scholz
la source
Que fait ~ personnage dans votre deuxième extrait?
serkan
@serkan, il s'agit d'un opérateur NOT au niveau du bit| , c'est une version courte d'obtenir à partir d'un index (avec -1) un résultat véridique / falsifié , si un index existe.
Nina Scholz
merci Nina, sans le caractère ~, le code fonctionne tel quel, n'est-ce pas?
serkan
@serkan, votre question n'est pas claire, mais sans ~elle ne fonctionne pas comme ça.
Nina Scholz
1
oh, !!(index = 0)et la !!~(index = 0)différence en effet. Merci!
serkan
4

Vous pouvez utiliser Array.prototype.some () de la manière suivante (comme mentionné dans les autres réponses):

https://jsfiddle.net/h1d69exj/2/

function findIndexInData(data, property, value) {
    var result = -1;
    data.some(function (item, i) {
        if (item[property] === value) {
            result = i;
            return true;
        }
    });
    return result;
}
var data = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]



alert(findIndexInData(data, 'prop2', "yutu")); // shows index of 1
GibboK
la source
4

J'ai vu de nombreuses solutions dans ce qui précède.

Ici, j'utilise la fonction de carte pour trouver l'index du texte de recherche dans un objet tableau.

Je vais expliquer ma réponse en utilisant les données des étudiants.

  • étape 1 : créer un objet tableau pour les étudiants (facultatif, vous pouvez créer votre propre objet tableau).
    var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

  • étape 2 : créer une variable pour rechercher du texte
    var studentNameToSearch = "Divya";

  • étape 3 : Créer une variable pour stocker l'index correspondant (ici, nous utilisons la fonction de carte pour itérer).
    var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

var students = [{name:"Rambabu",htno:"1245"},{name:"Divya",htno:"1246"},{name:"poojitha",htno:"1247"},{name:"magitha",htno:"1248"}];

var studentNameToSearch = "Divya";

var matchedIndex = students.map(function (obj) { return obj.name; }).indexOf(studentNameToSearch);

console.log(matchedIndex);

alert("Your search name index in array is:"+matchedIndex)

Rambabu Bommisetti
la source
3
function findIndexByKeyValue(_array, key, value) {
    for (var i = 0; i < _array.length; i++) { 
        if (_array[i][key] == value) {
            return i;
        }
    }
    return -1;
}
var a = [
    {prop1:"abc",prop2:"qwe"},
    {prop1:"bnmb",prop2:"yutu"},
    {prop1:"zxvz",prop2:"qwrq"}];
var index = findIndexByKeyValue(a, 'prop2', 'yutu');
console.log(index);
pranabesh chand
la source
1

Pourquoi ne voulez-vous pas répéter exactement? Les nouveaux Array.prototype.forEach sont parfaits à cet effet!

Vous pouvez utiliser un arbre de recherche binaire pour rechercher via un seul appel de méthode si vous le souhaitez. Il s'agit d'une implémentation soignée de l'arbre de recherche BTree et Red black dans JS - https://github.com/vadimg/js_bintrees - mais je ne sais pas si vous pouvez trouver l'index en même temps.

Rishabh
la source
1

Une étape en utilisant Array.reduce () - pas de jQuery

var items = [{id: 331}, {id: 220}, {id: 872}];

var searchIndexForId = 220;
var index = items.reduce(function(searchIndex, item, index){
  if(item.id === searchIndexForId) { 
    console.log('found!');
    searchIndex = index;
  }
  return searchIndex;
}, null);

retournera nullsi l'index n'a pas été trouvé.

SagiSergeNadir
la source
0
var list =  [
                {prop1:"abc",prop2:"qwe"},
                {prop1:"bnmb",prop2:"yutu"},
                {prop1:"zxvz",prop2:"qwrq"}
            ];

var findProp = p => {
    var index = -1;
    $.each(list, (i, o) => {
        if(o.prop2 == p) {
            index = i;
            return false; // break
        }
    });
    return index; // -1 == not found, else == index
}
Ruben Morales Felix
la source
0

Georg a déjà mentionné que ES6 avait Array.findIndex pour cela. Et certaines autres réponses sont une solution de contournement pour ES5 en utilisant la méthode Array.some.

Une approche plus élégante peut être

var index;
for(index = yourArray.length; index-- > 0 && yourArray[index].prop2 !== "yutu";);

Dans le même temps, je tiens à souligner que Array.some peut être implémenté avec une technique de recherche binaire ou efficace. Ainsi, il pourrait mieux fonctionner en boucle dans certains navigateurs.

Sanjoy
la source
0

Essayez ce code

var x = [{prop1:"abc",prop2:"qwe"},{prop1:"bnmb",prop2:"yutu"},{prop1:"zxvz",prop2:"qwrq"}]
let index = x.findIndex(x => x.prop1 === 'zxvz')
Trilok Singh
la source