Ottieni contatore / indice loop usando per ... di sintassi in JavaScript


318

Attenzione:

la domanda si applica ancora ai for…ofloop.> Non usare for…inper scorrere su una matrice , usala per scorrere sulle proprietà di un oggetto. Detto questo, questo


Comprendo che la for…insintassi di base in JavaScript è simile alla seguente:

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

Ma come posso ottenere il contatore / indice del loop ?

So che probabilmente potrei fare qualcosa del genere:

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

O anche il buon vecchio:

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

Ma preferirei usare il for-inciclo più semplice . Penso che abbiano un aspetto migliore e abbiano più senso.

C'è un modo più semplice o più elegante?


In Python è facile:

for i, obj in enumerate(myArray):
    print i

6
Non usare per ... in per array. E comunque, scorre i nomi delle proprietà, non i valori delle proprietà.
Felix Kling

1
È un array, non un oggetto, giusto? Quindi alert(obj)?
Rocket Hazmat,

Risposte:


547

for…inscorre i nomi delle proprietà, non i valori, e lo fa in un ordine non specificato (sì, anche dopo ES6). Non dovresti usarlo per scorrere su array. Per loro, c'è il forEachmetodo ES5 che passa sia il valore che l'indice alla funzione assegnata:

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

O ES6 Array.prototype.entries, che ora supporta le versioni correnti del browser:

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

Per gli iterabili in generale (dove useresti un for…ofciclo piuttosto che un for…in), non c'è nulla di integrato, tuttavia:

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

dimostrazione

Se davvero volessi dire for…in- enumerando le proprietà - avresti bisogno di un contatore aggiuntivo. Object.keys(obj).forEachpotrebbe funzionare, ma include solo proprietà proprie ; for…ininclude proprietà enumerabili ovunque nella catena del prototipo.


2
Oh va bene. Ero confuso. Pensavo che il for-in di JavaScript fosse lo stesso di Python. Grazie per il chiarimento.
hobbes3,

1
@quantumpotato: lets sono vars con ambito di blocco. constsono immutabili.
Ry-

1
questa è stata una risposta dettagliata, grazie per questo. Chiarito davvero tutto ciò che è stato discusso
Dheeraj Bhaskar,

1
domanda stupida, ma cosa significano% d e% s in realtà, o potrebbero essere qualsiasi lettera che voglio che siano?
Klewis,

2
@klewis: %dformatta un numero intero e %sformatta una stringa. Sono basati su printf . Una specifica è in corso su console.spec.whatwg.org/#formatter .
Ry-

163

In ES6, è utile utilizzarlo per - of loop. È possibile ottenere l'indice per di in questo modo

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

Si noti che Array.entries()restituisce un iteratore , che è ciò che gli consente di funzionare nel ciclo for-of; non confonderlo con Object.entries () , che restituisce una matrice di coppie chiave-valore.


9
Questa è una risposta molto migliore di quella accettata!
trusktr,

3
Penso che questa soluzione sia migliore di forEach one ... Usa il nome per ... della sintassi del loop e non devi usare una funzione separata. In altre parole, è sintatticamente migliore. L'OP sembra aver voluto questo.
u8y7541,

1
entries()restituisce un oggetto vuoto: {}. Qualche idea sul perché sarebbe? My arrayis a Array of Objects.
Joshua Pinter,

@JoshuaPinter prova Object.entries(array)invece diarray.entries()
tonyg

2
Dovrebbe farlo, Joshua - l'oggetto è un iteratore, un oggetto con un next()metodo che restituirà le voci successive nella matrice ogni volta che viene chiamato. Non ci sono dati (visibili) in esso; ottieni i dati nell'oggetto sottostante chiamando next(), cosa che fa il for-of dietro le quinte. cc @tonyg
Shog9

26

Cosa ne pensi di questo

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

Dove array.forEachquesto metodo ha un indexparametro che è l'indice dell'elemento corrente in fase di elaborazione nell'array.


1
migliore risposta qui
codepleb

4
La risposta scelta è stata pubblicata 6 anni prima di questa e contiene già la stessa cosa ...
Deiv

Foreach non è buono per l'ottimizzazione, poiché breaknon è disponibile.
smartworld-dm,

20

Soluzione per raccolte di piccoli array:

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

arr - ARRAY, obj - KEY dell'elemento corrente, i - COUNTER / INDEX

Avviso: Method keys () non è disponibile per IE versione <9, è necessario utilizzare il codice Polyfill . https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


7
Suggerirei: utilizzare invece un contatore, incrementarlo in loop.
Mayankcpdixit

2
Aggiungendo a mayankcpdixit, utilizzare invece un contatore perché indexOf potrebbe avere un impatto negativo sulle prestazioni.
Decano Liu,

1
Più grande è l'oggetto, più lento sarà. Questo non si ridimensiona.
weltschmerz,

2
Questo è inutilmente lento e complicato perché var i = 0;ed i++;è più corto ed efficiente. Inoltre non funziona per le proprietà enumerabili che non sono proprietà proprie.
Ry-

1
@trusktr: E se è richiesto ... non dovresti ancora usarlo. Basta cambiare il contatore quando si modifica la raccolta. Se non deve essere sul posto, fai invece una bella trasformazione funzionale.
Ry-

13

I cicli for-iter ripetono le proprietà di un oggetto. Non utilizzarli per gli array, anche se a volte funzionano.

Le proprietà dell'oggetto quindi non hanno indice, sono tutte uguali e non è necessario eseguirle in un ordine determinato. Se vuoi contare le proprietà, dovrai impostare il contatore extra (come hai fatto nel tuo primo esempio).

loop su un array:

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

loop sopra un oggetto:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}

3
Non fare mai (var i=0; i<a.length; i++)risorse sprecate. Usa(var i=0, var len = a.length; i<len; i++)
Félix Sanz il

16
@FelixSanz: risorse di scarto? Non c'è modo. Questa è una micro-ottimizzazione prematura che non è quasi mai necessaria, ed var i=0; i<a.length; i++)è comunque il modello di loop standard ottimizzato da ogni motore javascript decente.
Bergi,

3
@FelixSanz: Sì, ed var i=0; i<a.length; i++è la migliore pratica.
Bergi,

1
BACIO . Se scrivi loop in cui ne hai davvero bisogno, o stai facendo qualcosa di sbagliato, o hai un argomento migliore per la sua necessità rispetto alla "migliore pratica". Sì, è una pratica standard, ma non per l'ottimizzazione delle prestazioni generiche, ma solo per la micro-ottimizzazione.
Bergi,

3
KISS si applica ovunque. L'ottimizzazione precoce è un anti-pratica.
Bergi,

7

Come altri hanno già detto, non dovresti usare for..in per scorrere su un array.

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

Se si desidera una sintassi più pulita, è possibile utilizzare per ogni:

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

Se si desidera utilizzare questo metodo, assicurarsi di includere lo shim ES5 per aggiungere il supporto per i browser meno recenti.


2

Risposta data da rushUp È corretta ma sarà più conveniente

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

1

Ecco una funzione eachWithIndexche funziona con qualsiasi cosa iterabile.

Puoi anche scrivere una funzione simile eachWithKeyche funziona con objets usando 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)

La cosa buona con i generatori è che sono pigri e possono prendere il risultato di un altro generatore come argomento.


1

Questa è la mia versione di un iteratore composito che produce un indice e il valore di qualsiasi funzione del generatore passato con un esempio di ricerca (lenta) per primi:

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
}


Perché hai una funzione eachWithIndex[Symbol.iterator]anziché solo una funzione eachWithIndex? eachWithIndexnon soddisfa l'interfaccia iterabile, che è il punto centrale di Symbol.iterator.
Ry-

@ Ry- Buona cattura, modificato eachWithIndexper accettare iterabile e restituire un composito chiuso iterabile.
Akurtser

1

Oltre alle ottime risposte pubblicate da tutti, voglio aggiungere che la soluzione più efficace è ES6 entries. Sembra controintuitivo per molti sviluppatori qui, quindi ho creato questo perfetto benchamrk .

inserisci qui la descrizione dell'immagine

È ~ 6 volte più veloce. Principalmente perché non è necessario: a) accedere all'array più di una volta e, b) eseguire il cast dell'indice.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.