indexOf-Methode in einem Objektarray?

513

Was ist die beste Methode, um den Index eines Arrays zu erhalten, das Objekte enthält?

Stellen Sie sich dieses Szenario vor:

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

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

Jetzt möchte ich das indexOfObjekt haben, welche helloEigenschaft 'stevie'welche ist, in diesem Beispiel 1.

Ich bin ein ziemlich Neuling in JavaScript und ich weiß nicht, ob es eine einfache Methode gibt oder ob ich meine eigene Funktion dafür erstellen sollte.

Antonio Laguna
quelle
1
Möchten Sie die beiden Objekte zusammenführen hellound qaz?
Armin
Nein, ich nicht. Ich möchte eine Liste von Objekten in einem Array haben.
Antonio Laguna
Ah okay! Sie möchten die Position des gesamten Objekts im Array kennen, das eine definierte Eigenschaft hat.
Armin
10
Ich habe eine sehr einfache Funktion gefunden, um genau dieses Problem mit dieser SO-Antwort zu lösen: 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 ist besser als akzeptierte Antwort (wenn ES6 für Sie funktioniert) - siehe vollständiges Beispiel unten
yar1

Antworten:

1043

Ich denke, Sie können es in einer Zeile mit der Kartenfunktion lösen :

pos = myArray.map(function(e) { return e.hello; }).indexOf('stevie');
Pablo Francisco Pérez Hidalgo
quelle
58
Dies sollte ehrlich gesagt die akzeptierte Antwort sein. Die meisten Browser unterstützen heutzutageArray.prototype.map()
AlbertEngelB
10
Es wird von IE8 nicht unterstützt, aber wenn dies kein Problem ist, ist dies die beste Lösung.
Antonio Laguna
57
Ähm ... ist es nicht erwähnenswert, dass Array.prototype.map () ein ganz neues Array erstellt, das die zugeordneten Elemente enthält? Wenn Sie also ein Array mit 1000 Elementen haben, haben Sie zuerst ein anderes Array mit 1000 Elementen erstellt und dann danach gesucht? Ich denke, es lohnt sich, die Leistung dieser Methode im Vergleich zu einer einfachen for-Schleife zu sehen. Besonders wenn Sie auf einer mobilen Plattform mit begrenzten Ressourcen arbeiten.
Doug
7
@Doug Obwohl Ihr Standpunkt zur Leistung in der Tat richtig ist, wer würde bei klarem Verstand eine Codezeile durch sieben ersetzen für eine Anwendung, die fast per Definition an E / A / Netzwerk gebunden ist, bis sie ein Profil für Engpässe erstellt hat?
Jared Smith
6
Technisch gesehen ist eine minimierte js-Datei auch eine Zeile; D
GreaterKing
371

Array.prototype.findIndex wird in allen Browsern außer IE (ohne Edge) unterstützt. Aber die bereitgestellte Polyfüllung ist schön.

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

Die Lösung mit Karte ist okay. Sie iterieren jedoch bei jeder Suche über das gesamte Array. Dies ist nur der schlechteste Fall für findIndex, der die Iteration beendet, sobald eine Übereinstimmung gefunden wurde.


Es gibt keinen wirklich prägnanten Weg (als Entwickler sich um IE8 sorgen mussten) , aber hier ist eine gängige Lösung:

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

oder als Funktion:

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

Nur ein paar Anmerkungen:

  1. Nicht für ... in Schleifen auf Arrays verwenden
  2. Stellen Sie sicher, dass Sie aus der Schleife ausbrechen oder aus der Funktion zurückkehren, sobald Sie Ihre "Nadel" gefunden haben.
  3. Seien Sie vorsichtig mit der Objektgleichheit

Zum Beispiel,

var a = {obj: 0};
var b = [a];
b.indexOf({obj: 0}); // -1 not found
Joe
quelle
Die Funktion hat den Suchvergleich falsch, da es searchTerm sein sollte :)
Antonio Laguna
Es gab mehrere Vorkommen
Joe
3
@SteveBennett ist eine leistungsoptimierte Version; Die Länge des Arrays muss nur einmal bestimmt werden (wenn die Variablen für die for-Schleife initialisiert werden). In Ihrem Fall wird die Länge bei jeder Iteration erneut überprüft. Siehe auch stackoverflow.com/questions/5349425/… und stackoverflow.com/questions/8452317/… . Wenn die Leistung jedoch nicht hoch ist, spielt dies keine Rolle.
Loother
1
Gute Antwort, aber ich habe ein Performance-Benchmarking durchgeführt (siehe jsperf.com/find-index-of-object-in-array-by-contents ) und festgestellt, dass die hier erwähnte funktionsbasierte Antwort die zweithäufigste Antwort zu sein scheint. Das einzige, was performanter ist, ist, es in einen Prototyp zu setzen, anstatt nur in eine Funktion, wie in meiner Antwort erwähnt .
Uniphonic
123

In ES2015 ist dies ziemlich einfach:

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

oder wahrscheinlich mit besserer Leistung für größere Arrays:

myArray.findIndex(x => x.hello === 'stevie')
Steve Bennett
quelle
2
Guter Ansatz zur Verwendung von ES6
kag
Ich war überrascht, dass keine dieser Methoden so performant ist wie der Prototyp für die Schleife, wie in meiner Antwort erwähnt. Obwohl die Browserunterstützung für die findIndex-Methode etwas schlecht ist, scheint es, als würde sie dasselbe tun, aber dennoch weniger leistungsfähig sein? Siehe den Link in meiner Antwort für Benchmarks.
Uniphonic
Gut zu wissen, ob Leistung für die jeweilige Aufgabe wichtig ist. Es ist meiner Erfahrung nach sehr selten, aber ymmv.
Steve Bennett
24
var idx = myArray.reduce( function( cur, val, index ){

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

}, -1 );
Esailija
quelle
17

Ich mag Pablos Antwort, aber Array # indexOf und Array # map funktionieren nicht in allen Browsern. Unterstrich verwendet nativen Code, sofern dieser verfügbar ist, weist jedoch auch Fallbacks auf. Außerdem verfügt es über die Zupfmethode, mit der genau das ausgeführt wird, was die anonyme Kartenmethode von Pablo bewirkt.

var idx = _.chain(myArray).pluck("hello").indexOf("Stevie").value();
Tandrewnichole
quelle
1
Array.prototype.map () wird für IE9 + unterstützt und Sie können eine Polyfüllung für IE8, 7, 6 verwenden: developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/…
Johann Echavarria
1
Sie könnten eine Polyfüllung verwenden. . . oder Sie könnten einfach Unterstrich oder Lodash verwenden, bei denen es sich im Grunde um Polyfills handelt, an denen eine ganze Reihe anderer Leckereien angebracht sind . Was ist der Einwand mit Unterstrich? Größe?
Tandrewnichols
Ich mag Underscore wirklich, Ihre Antwort ist auch klug, aber IMHO Pablos Antwort ist die sauberste.
Johann Echavarria
Wow, ich hätte nie gedacht, so eine Verkettung zu verwenden. Mir gefällt sehr gut, wie die Suche dadurch flüssig wird.
Dylan Pierce
chainist hier überflüssig. _.pluck(myArray, 'hello').indexOf('stevie')
Steve Bennett
14

Oder Prototyp:

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");
Nathan Zaetta
quelle
9
Sehr angenehm! Obwohl ich prototype.indexOfObject wählen würde, um die vorhandene Array.indexOf-Methode nicht zu beeinträchtigen. 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
Ich würde es in einen selbstausführenden Verschluss einwickeln, wobei das alte vorher gespeichert wird, wobei die erste Zeile der Ersetzungsfunktion etwas in der Art von ist if (typeof property === 'string' || typeof property === 'number' || typeof property === 'boolean') return oldIndexOf(property, value);. Dies liegt daran, dass dies die wenigen Typen sind, die unveränderlich sind. Ich würde auch ein drittes Argument nennen, um bei Bedarf einen Fallback auf die native Methode zu ermöglichen.
Isiah Meadows
8

Kurz

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

Anwendungsfälle :

  /*****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);
      }
    };
Abdennour TOUMI
quelle
6

Ich habe hier einige Leistungstests mit verschiedenen Antworten durchgeführt, die jeder selbst ausführen kann:

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

Basierend auf meinen ersten Tests in Chrome ist die folgende Methode (unter Verwendung einer in einem Prototyp eingerichteten for-Schleife) die schnellste:

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

Dieser Code ist eine leicht modifizierte Version von Nathan Zaettas Antwort.

In den Leistungsbenchmarks habe ich es versucht, wobei sich das Ziel sowohl in der Mitte (Index 500) als auch am Ende (Index 999) eines 1000-Objekt-Arrays befand, und selbst wenn ich das Ziel als allerletztes Element in das Array eingefügt habe (dh) dass es jedes einzelne Element im Array durchlaufen muss, bevor es gefunden wird), endet es immer noch am schnellsten.

Diese Lösung hat auch den Vorteil, dass sie eine der knappsten für die wiederholte Ausführung ist, da nur die letzte Zeile wiederholt werden muss:

myArray.indexOfObject("hello", "stevie");
Uniphonisch
quelle
2
Ich wollte gerade eine Antwort auf diese Frage mit einer Geige mit meinen eigenen Tests posten, aber dank Ihrer Antwort muss ich nicht mehr. Ich möchte nur Ihre Tests bestätigen - ich habe das gleiche Ergebnis erzielt, aber eine whileSchleife anstelle foreiner verwendet, und performance.now(). Ich wünschte, diese Antwort wäre mehr positiv bewertet worden und ich hätte sie früher gesehen, es hätte mir einige Zeit gespart ...
Yin Cognyto
5

Ich habe mehrere Methoden verglichen und ein Ergebnis mit dem schnellsten Weg zur Lösung dieses Problems erhalten. Es ist einfor Schleife. Es ist mehr als fünfmal schneller als jede andere Methode.

Hier ist die Testseite: https://jsbench.me/9hjewv6a98

John Klimov
quelle
Fehlt eine for-ofSchleife nein?
Douglas Gaskell
5

Die meisten anderen Antworten hier sind gültig. Manchmal ist es am besten, eine kurze einfache Funktion in der Nähe des Verwendungsortes zu erstellen.

// 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');

Es kommt darauf an, was Ihnen wichtig ist. Es kann Codezeilen speichern und sehr klug sein, einen Einzeiler zu verwenden oder eine generische Lösung zu platzieren, die verschiedene Randfälle abdeckt. Aber manchmal ist es besser, einfach zu sagen: "Hier habe ich es so gemacht", als zukünftige Entwickler zusätzlichen Reverse Engineering-Arbeiten zu überlassen. Vor allem, wenn Sie sich wie in Ihrer Frage als "Neuling" betrachten.

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

Kümmere dich um das [0].

Es ist richtig zu verwenden reducewie inAntonio Laguna der Antwort zu verwenden.

Entschuldigung für die Kürze ...

Cody
quelle
4

Wenn Ihr Objekt dasselbe Objekt ist wie das, das Sie innerhalb des Arrays verwenden, sollten Sie in der Lage sein, den Index des Objekts auf dieselbe Weise abzurufen, als wäre es eine Zeichenfolge.

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
Caio Koiti
quelle
Dies ist die Antwort, nach der ich gesucht habe, da mir nicht klar war, ob IndexOf nach Wert übereinstimmt oder was. Jetzt weiß ich, dass ich IndexOf verwenden kann, um mein Objekt zu finden, und mache mir keine Sorgen, wenn es andere Objekte mit denselben Eigenschaften gibt.
MDave
3

einfach:

myArray.indexOf(myArray.filter(function(item) {
    return item.hello == "stevie"
})[0])
Gänsegeier
quelle
3

Wenn Sie nur daran interessiert sind, die Position zu finden, lesen Sie die Antwort von @ Pablo .

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

Wenn Sie sich jedoch darauf freuen , das Element zu finden (dh wenn Sie daran gedacht haben, so etwas zu tun myArray[pos]), gibt es eine effizientere einzeilige Möglichkeit, dies zu tun filter.

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

Siehe Leistungsergebnisse (~ + 42% ops / s): http://jsbench.github.io/#7fa01f89a5dc5cc3bee79abfde80cdb3

zurfyx
quelle
2

Siehe dieses Beispiel: http://jsfiddle.net/89C54/

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

Es beginnt mit Null zu zählen.

Armin
quelle
2

Ich habe eine generische Funktion erstellt, um zu überprüfen, ob der folgende Code für jedes Objekt funktioniert

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

Die erste Warnung gibt -1 zurück (bedeutet Übereinstimmung nicht gefunden) und die zweite Warnung gibt 0 zurück (bedeutet Übereinstimmung gefunden).

Shiljo Paulson
quelle
2

Verwendung _.findIndexaus der Bibliothek underscore.js

Hier ist das Beispiel _.findIndex([{a:1},{a: 2,c:10},{a: 3}], {a:2,c:10}) //1

niren
quelle
Wenn Sie Methoden aus zusätzlichen Bibliotheken vorschlagen, sollten Sie angeben, woher sie stammen.
Craicerjack
@ Steve Bennett nette Nutzung. Der Bibliothek ist jetzt in lodash
zabusa
2

Mit der ES6- findIndexMethode können Sie ohne lodash oder andere Bibliotheken schreiben:

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

Dadurch werden die unmittelbaren Eigenschaften des Objekts verglichen, jedoch nicht in die Eigenschaften zurückgeführt.

Wenn Ihre Implementierung noch keine bereitstellt findIndex(die meisten nicht), können Sie eine leichte Polyfüllung hinzufügen, die diese Suche unterstützt:

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

(Aus meiner Antwort auf diesen Betrug )

ssube
quelle
2

Sie können Array.prototype.findIndex()grundsätzlich eine native und bequeme Funktion verwenden :

Die Methode findIndex () gibt einen Index im Array zurück, wenn ein Element im Array die bereitgestellte Testfunktion erfüllt. Andernfalls wird -1 zurückgegeben.

Nur ein Hinweis, es wird in Internet Explorer, Opera und Safari nicht unterstützt, aber Sie können eine Polyfüllung verwenden, die unter dem folgenden Link bereitgestellt wird.

Mehr Informationen:

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

GibboK
quelle
2

Weitere Antwort von @Monika Garg können Sie verwenden findIndex()(Es gibt eine Polyfüllung für unsupprted Browser).

Ich habe gesehen, dass die Leute diese Antwort abgelehnt haben, und ich hoffe, dass sie dies aufgrund der falschen Syntax getan haben, weil dies meiner Meinung nach der eleganteste Weg ist.

Die Methode findIndex () gibt einen Index im Array zurück, wenn ein Element im Array die bereitgestellte Testfunktion erfüllt. Andernfalls wird -1 zurückgegeben.

Zum Beispiel:

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

Mosh Feu
quelle
Die Methode wirkt elegant, aber laut IEN keine IE-Unterstützung? developer.mozilla.org/en/docs/Web/JavaScript/Reference/…
Jaakko Karhu
Versuchen Sie, die Polyfüllung zu verwenden (Link in der Antwort).
Mosh Feu
1

Auf diese Weise finden Sie den Index des Objekts im Array

    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;
        }
    }
Asad Fida
quelle
0

Dies funktioniert ohne benutzerdefinierten Code

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

Hinweis: Dies gibt nicht den tatsächlichen Index an, sondern gibt nur an, ob Ihr Objekt in der aktuellen Datenstruktur vorhanden ist

Xeltor
quelle
1
Dies ist nicht gültig, da dies keinen Index zulässt
Antonio Laguna
0

Sie können einfach verwenden

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

Kein Unterstrich, nein für, nur eine Reduzierung.

7ynk3r
quelle
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"));
user3235365
quelle
Verwenden Sie eine Array-Methode.
user3235365
-1

Sie können dazu Ihren eigenen Prototyp erstellen:

etwas wie:

Array.prototype.indexOfObject = function (object) {
    for (var i = 0; i < this.length; i++) {
        if (JSON.stringify(this[i]) === JSON.stringify(object))
            return i;
    }
}
Janx aus Venezuela
quelle
2
Schlechte Praxis, Kapselung brechen: developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/…
HMR
Dies würde auch bei rekursiv definierten Objekten brechen.
Joseph Coco
-2

Ich werde es vorziehen, findIndex()Methode zu verwenden :

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

index gibt Ihnen die Indexnummer.

Monika Garg
quelle
1
Antwort, Rechtschreibung und Codeeinzug und :) sind falsch?
Sachin Verma
1
findIndex ist in keiner Javascript-Standardimplementierung enthalten. Es gibt einen bevorstehenden (ecma 6) Vorschlag für eine solche Methode, aber ihre Unterschrift ist nicht so. Bitte klären Sie, was Sie meinen (möglicherweise den Namen der Methode), geben Sie die findIndex-Methodendeklaration an oder benennen Sie die Bibliothek, die Sie verwenden.
Sebastien F.