Holen Sie sich den Schleifenzähler / Index mit for… der Syntax in JavaScript

315

Vorsicht:

Die Frage gilt weiterhin für for…ofSchleifen.> Nicht for…inzum Durchlaufen eines Arrays verwenden, sondern zum Durchlaufen der Eigenschaften eines Objekts. Das heißt, das


Ich verstehe, dass die grundlegende for…inSyntax in JavaScript folgendermaßen aussieht:

for (var obj in myArray) {
    // ...
}

Aber wie bekomme ich den Schleifenzähler / Index ?

Ich weiß, ich könnte wahrscheinlich so etwas tun:

var i = 0;
for (var obj in myArray) {
    alert(i)
    i++
}

Oder sogar die guten Alten:

for (var i = 0; i < myArray.length; i++) {
    var obj = myArray[i]
    alert(i)
}

Aber ich würde lieber die einfachere for-inSchleife verwenden. Ich denke, sie sehen besser aus und machen mehr Sinn.

Gibt es einen einfacheren oder eleganteren Weg?


In Python ist es einfach:

for i, obj in enumerate(myArray):
    print i
hobbes3
quelle
6
Nicht für ... in für Arrays verwenden. Auf jeden Fall werden die Eigenschaftsnamen und nicht die Werte der Eigenschaften durchlaufen.
Felix Kling
1
Es ist ein Array, kein Objekt, oder? Also alert(obj)?
Rocket Hazmat

Antworten:

542

for…initeriert über Eigenschaftsnamen, nicht über Werte, und zwar in einer nicht angegebenen Reihenfolge (ja, auch nach ES6). Sie sollten es nicht verwenden, um über Arrays zu iterieren. Für sie gibt es die ES5- forEachMethode, die sowohl den Wert als auch den Index an die von Ihnen angegebene Funktion übergibt:

var myArray = [123, 15, 187, 32];

myArray.forEach(function (value, i) {
    console.log('%d: %s', i, value);
});

// Outputs:
// 0: 123
// 1: 15
// 2: 187
// 3: 32

Oder ES6s Array.prototype.entries, die jetzt in allen aktuellen Browserversionen unterstützt werden:

for (const [i, value] of myArray.entries()) {
    console.log('%d: %s', i, value);
}

Für Iterables im Allgemeinen (bei denen Sie for…ofeher eine Schleife als eine verwenden würden for…in) ist jedoch nichts integriert:

function* enumerate(iterable) {
    let i = 0;

    for (const x of iterable) {
        yield [i, x];
        i++;
    }
}

for (const [i, obj] of enumerate(myArray)) {
    console.log(i, obj);
}

Demo

Wenn Sie tatsächlich gemeint hätten for…in- Eigenschaften aufzählen -, würden Sie einen zusätzlichen Zähler benötigen. Object.keys(obj).forEachkönnte funktionieren, enthält aber nur eigene Eigenschaften; for…inEnthält unzählige Eigenschaften in der gesamten Prototypenkette.

Ry-
quelle
2
Oh ok. Ich war verwirrt. Ich dachte, das For-In von JavaScript sei das gleiche wie das von Python. Danke für die Klarstellung.
hobbes3
1
@quantumpotato: lets sind vars mit Blockbereich. consts sind unveränderlich.
Ry-
1
Dies war eine ausführliche Antwort, danke dafür. Wirklich alle besprochenen Dinge geklärt
Dheeraj Bhaskar
1
dumme Frage, aber wofür stehen% d und% s eigentlich, oder könnten sie irgendein Buchstabe sein, den ich möchte?
Klewis
2
@klewis: Formatiert %deine Ganzzahl und %seine Zeichenfolge. Sie basieren auf printf . Unter console.spec.whatwg.org/#formatter wird eine Spezifikation ausgeführt .
Ry-
161

In ES6 ist es gut, die for-of-Schleife zu verwenden. Sie können Index für so etwas bekommen

for (let [index, val] of array.entries()) {
        // your code goes here    
}

Beachten Sie, dass Array.entries()gibt einen Iterator , das ist , was es in der for-Schleife von arbeiten können; Verwechseln Sie dies nicht mit Object.entries () , das ein Array von Schlüssel-Wert-Paaren zurückgibt .

RushUp
quelle
9
Dies ist eine viel bessere Antwort als die akzeptierte!
Trusktr
3
Ich denke, diese Lösung ist besser als die forEach-Lösung ... Sie verwendet das Nominal für ... der Schleifensyntax, und Sie müssen keine separate Funktion verwenden. Mit anderen Worten, es ist syntaktisch besser. Das OP scheint dies gewollt zu haben.
u8y7541
1
entries()gibt ein leeres Objekt zurück : {}. Irgendeine Idee warum das so wäre? Mein arrayist ein Array von Objekten.
Joshua Pinter
@ JoshuaPinter versuchen Object.entries(array)stattarray.entries()
Tonyg
2
Das soll es tun, Joshua - das Objekt ist ein Iterator, ein Objekt mit einer next()Methode, die bei jedem Aufruf nachfolgende Einträge im Array zurückgibt. Es sind keine (sichtbaren) Daten darin; Sie erhalten die Daten im zugrunde liegenden Objekt durch Aufrufen next(), was for-of hinter den Kulissen tut. cc @tonyg
Shog9
26

Wie wäre es damit

let numbers = [1,2,3,4,5]
numbers.forEach((number, index) => console.log(`${index}:${number}`))

Wobei array.forEachdiese Methode einen indexParameter hat, der der Index des aktuellen Elements ist, das im Array verarbeitet wird.

Sanjay Shr
quelle
1
beste Antwort hier
Codepleb
4
Die gewählte Antwort wurde 6 Jahre vor dieser veröffentlicht und enthält das Gleiche bereits ...
Deiv
Foreach ist nicht gut für die Optimierung, da breakes nicht verfügbar ist.
smartworld-dm
19

Lösung für kleine Array-Sammlungen:

for (var obj in arr) {
    var i = Object.keys(arr).indexOf(obj);
}

arr - ARRAY, obj - KEY des aktuellen Elements, i - COUNTER / INDEX

Hinweis: Method keys () ist für IE-Version <9 nicht verfügbar. Sie sollten Polyfill- Code verwenden. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

iwasborntobleed
quelle
7
Ich würde vorschlagen: Verwenden Sie stattdessen einen Zähler und erhöhen Sie ihn in einer Schleife.
Mayankcpdixit
2
Verwenden Sie stattdessen einen Zähler, wenn Sie zu mayankcpdixit hinzufügen, da indexOf negative Auswirkungen auf die Leistung haben kann.
Dean Liu
1
Je größer das Objekt, desto langsamer wird es. Dies skaliert nicht.
Dchacke
2
Dies ist eine Art von pointlessly langsam und kompliziert , weil var i = 0;und i++;kürzer und effizienter zu gestalten . Außerdem funktioniert es nicht für aufzählbare Eigenschaften, die keine eigenen Eigenschaften sind.
Ry-
1
@trusktr: Und wenn es erforderlich ist ... sollten Sie dies immer noch nicht verwenden. Ändern Sie einfach den Zähler, wenn Sie die Sammlung ändern. Wenn es nicht vorhanden sein muss, führen Sie stattdessen eine nette funktionale Transformation durch.
Ry-
13

For-in-Loops durchlaufen die Eigenschaften eines Objekts. Verwenden Sie sie nicht für Arrays, auch wenn sie manchmal funktionieren.

Objekteigenschaften haben dann keinen Index, sie sind alle gleich und müssen nicht in einer bestimmten Reihenfolge durchlaufen werden. Wenn Sie Eigenschaften zählen möchten, müssen Sie den zusätzlichen Zähler einrichten (wie in Ihrem ersten Beispiel).

Schleife über ein Array:

var a = [];
for (var i=0; i<a.length; i++) {
    i // is the index
    a[i] // is the item
}

Schleife über ein Objekt:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}
Bergi
quelle
3
Tun Sie niemals, was (var i=0; i<a.length; i++)verschwendete Ressourcen sind. Verwenden Sie(var i=0, var len = a.length; i<len; i++)
Félix Sanz
16
@ FelixSanz: Ressourcen verschwenden? Auf keinen Fall. Dies ist eine vorzeitige Mikrooptimierung, die kaum jemals erforderlich ist, und var i=0; i<a.length; i++)das Standardschleifenmuster, das ohnehin von jeder anständigen Javascript-Engine optimiert wird.
Bergi
3
@ FelixSanz: Ja, und das var i=0; i<a.length; i++ist die beste Vorgehensweise.
Bergi
1
KUSS . Wenn Sie Schleifen schreiben, in denen Sie dies wirklich brauchen, machen Sie entweder etwas falsch oder Sie haben ein besseres Argument für die Notwendigkeit als "Best Practice". Ja, dies ist eine Standardpraxis, jedoch nicht zur allgemeinen Leistungsoptimierung, sondern nur zur Mikrooptimierung.
Bergi
3
KISS gilt überall. Vorzeitige Optimierung ist eine Anti-Praxis.
Bergi
7

Wie andere gesagt haben, sollten Sie for..in nicht verwenden, um über ein Array zu iterieren.

for ( var i = 0, len = myArray.length; i < len; i++ ) { ... }

Wenn Sie eine sauberere Syntax wünschen, können Sie forEach verwenden:

myArray.forEach( function ( val, i ) { ... } );

Wenn Sie diese Methode verwenden möchten, stellen Sie sicher, dass Sie das ES5-Shim einschließen, um Unterstützung für ältere Browser hinzuzufügen.

Robert Messerle
quelle
2

Antwort von RushUp Ist richtig, aber dies ist bequemer

for (let [index, val] of array.entries() || []) {
   // your code goes here    
}
Renish Gotecha
quelle
1

Hier ist eine Funktion eachWithIndex, die mit allem iterierbaren funktioniert .

Sie können auch eine ähnliche Funktion schreiben eachWithKey, die mit objets funktioniert for...in.

// example generator (returns an iterator that can only be iterated once)
function* eachFromTo(start, end) { for (let i = start; i <= end; i++) yield i }

// convers an iterable to an array (potential infinite loop)
function eachToArray(iterable) {
    const result = []
    for (const val of iterable) result.push(val)
    return result
}

// yields every value and index of an iterable (array, generator, ...)
function* eachWithIndex(iterable) {
    const shared = new Array(2)
    shared[1] = 0
    for (shared[0] of iterable) {
        yield shared
        shared[1]++
    }
}

console.log('iterate values and indexes from a generator')
for (const [val, i] of eachWithIndex(eachFromTo(10, 13))) console.log(val, i)

console.log('create an array')
const anArray = eachToArray(eachFromTo(10, 13))
console.log(anArray)

console.log('iterate values and indexes from an array')
for (const [val, i] of eachWithIndex(anArray)) console.log(val, i)

Das Gute an Generatoren ist, dass sie faul sind und das Ergebnis eines anderen Generators als Argument heranziehen können.

Rivenfall
quelle
1

Das ist meine Version eines zusammengesetzten Iterators, der einen Index und den Wert einer übergebenen Generatorfunktion mit einem Beispiel für eine (langsame) Primzahlsuche liefert:

const eachWithIndex = (iterable) => {
  return {
    *[Symbol.iterator]() {
      let i = 0
      for(let val of iteratable) {
        i++
          yield [i, val]
      }
    }
  }

}

const isPrime = (n) => {
  for (i = 2; i < Math.floor(Math.sqrt(n) + 1); i++) {
    if (n % i == 0) {
      return false
    }
  }
  return true
}

let primes = {
  *[Symbol.iterator]() {
    let candidate = 2
    while (true) {
      if (isPrime(candidate)) yield candidate
        candidate++
    }
  }
}

for (const [i, prime] of eachWithIndex(primes)) {
  console.log(i, prime)
  if (i === 100) break
}

akurtser
quelle
Warum haben Sie eine Funktion eachWithIndex[Symbol.iterator]statt nur eine Funktion eachWithIndex? eachWithIndexerfüllt nicht die iterierbare Schnittstelle, die der springende Punkt ist Symbol.iterator.
Ry-
@ Ry- Guter Fang, geändert eachWithIndex, um iterable zu akzeptieren und eine geschlossene zusammengesetzte iterable zurückzugeben.
Akurtser
1

Zusätzlich zu den sehr guten Antworten, die alle gepostet haben, möchte ich hinzufügen, dass die leistungsstärkste Lösung die ES6 ist entries. Es scheint für viele Entwickler hier kontraintuitiv zu sein, also habe ich diesen perfekten Benchamrk erstellt .

Geben Sie hier die Bildbeschreibung ein

Es ist ~ 6 mal schneller. Hauptsächlich, weil nicht: a) mehr als einmal auf das Array zugegriffen werden muss und b) der Index umgewandelt werden muss.

sospedra
quelle