Récupère le compteur / index de boucle en utilisant for… de la syntaxe en JavaScript

318

Mise en garde:

question s'applique toujours aux for…ofboucles.> Ne pas utiliser for…inpour itérer sur un tableau , utilisez-le pour itérer sur les propriétés d'un objet. Cela dit, ce


Je comprends que la for…insyntaxe de base en JavaScript ressemble à ceci:

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

Mais comment obtenir le compteur / index de boucle ?

Je sais que je pourrais probablement faire quelque chose comme:

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

Ou même le bon vieux:

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

Mais je préfère utiliser la for-inboucle plus simple . Je pense qu'ils sont plus beaux et plus sensés.

Existe-t-il une manière plus simple ou plus élégante?


En Python, c'est simple:

for i, obj in enumerate(myArray):
    print i
hobbes3
la source
6
Ne pas utiliser pour ... dans pour les tableaux. Et de toute façon, il itère sur les noms de propriété, pas sur les valeurs des propriétés.
Felix Kling
1
C'est un tableau, pas un objet, non? Alors alert(obj),?
Rocket Hazmat

Réponses:

548

for…initère sur les noms de propriété, pas sur les valeurs, et le fait dans un ordre non spécifié (oui, même après ES6). Vous ne devez pas l'utiliser pour parcourir les tableaux. Pour eux, il y a la forEachméthode ES5 qui transmet à la fois la valeur et l'index à la fonction que vous lui donnez:

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

Ou ES6 Array.prototype.entries, qui prend désormais en charge les versions de navigateur actuelles:

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

Pour les itérables en général (où vous utiliseriez une for…ofboucle plutôt qu'une for…in), il n'y a cependant rien de intégré:

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

démo

Si vous vouliez réellement for…in- énumérer les propriétés - vous auriez besoin d'un compteur supplémentaire. Object.keys(obj).forEachpourrait fonctionner, mais il ne comprend que ses propres propriétés; for…ininclut des propriétés énumérables n'importe où sur la chaîne du prototype.

Ry-
la source
2
Ah d'accord. J'étais confus. Je pensais que le for-in de JavaScript était le même que celui de Python. Merci pour la clarification.
hobbes3
1
@quantumpotato: lets sont des vars avec une portée de bloc. consts sont immuables.
Ry-
1
ce fut une réponse détaillée, merci pour cela. Vraiment clarifié toutes les choses discutées
Dheeraj Bhaskar
1
question stupide, mais que représentent réellement% d et% s, ou pourraient-ils être une lettre que je veux qu’ils soient?
klewis
2
@klewis: %dformate un entier et %sformate une chaîne. Ils sont basés sur printf . Une spécification est en cours sur console.spec.whatwg.org/#formatter .
Ry-
163

Dans ES6, il est bon d'utiliser la boucle for-of. Vous pouvez obtenir un index pour comme ceci

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

Notez que Array.entries()retourne un itérateur , ce qui lui permet de fonctionner dans la boucle for-of; ne confondez pas cela avec Object.entries () , qui retourne un tableau de paires clé-valeur.

accourir
la source
9
C'est une bien meilleure réponse que celle acceptée!
trusktr
3
Je pense que cette solution est meilleure que la forEach ... Elle utilise le nomal pour ... de la syntaxe de boucle, et vous n'avez pas besoin d'utiliser une fonction distincte. En d'autres termes, c'est syntaxiquement meilleur. Le PO semble avoir voulu cela.
u8y7541
1
entries()retourne un objet vide: {}. Une idée pourquoi ce serait? My arrayest un tableau d'objets.
Joshua Pinter
@JoshuaPinter essayez Object.entries(array)au lieu dearray.entries()
tonyg
2
C'est censé faire cela, Joshua - l'objet est un itérateur, un objet avec une next()méthode qui retournera les entrées suivantes dans le tableau chaque fois qu'il sera appelé. Il n'y a aucune donnée (visible) dedans; vous obtenez les données dans l'objet sous-jacent en appelant next(), ce qui fait for-of dans les coulisses. cc @tonyg
Shog9
26

Que dis-tu de ça

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

array.forEachcette méthode a un indexparamètre qui est l'index de l'élément en cours de traitement dans le tableau.

Sanjay Shr
la source
1
meilleure réponse ici
codepleb
4
La réponse choisie a été postée 6 ans avant celle-ci et contient déjà la même chose ...
Deiv
Foreach n'est pas bon pour l'optimisation, car il breakn'est pas disponible.
smartworld-dm
20

Solution pour les collections de petites baies:

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

arr - ARRAY, obj - KEY of current element, i - COUNTER / INDEX

Remarque: les clés de méthode () ne sont pas disponibles pour IE version <9, vous devez utiliser le code Polyfill . https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

iwasborntobleed
la source
7
Je suggère: utilisez plutôt un compteur, incrémentez-le en boucle.
mayankcpdixit
2
En ajoutant à mayankcpdixit, utilisez un compteur à la place, car indexOf pourrait avoir un impact négatif sur les performances.
Dean Liu
1
Plus l'objet est grand, plus cela ralentira. Cela ne change pas d'échelle.
weltschmerz
2
C'est un peu inutile et compliqué car var i = 0;il i++;est plus court et plus efficace. De plus, cela ne fonctionne pas pour les propriétés énumérables qui ne sont pas des propriétés propres.
Ry-
1
@trusktr: Et si cela est nécessaire… vous ne devriez toujours pas l'utiliser. Modifiez simplement le compteur lorsque vous modifiez la collection. S'il ne doit pas être en place, effectuez plutôt une belle transformation fonctionnelle.
Ry-
13

Les boucles en boucle parcourent les propriétés d'un objet. Ne les utilisez pas pour les tableaux, même s'ils fonctionnent parfois.

Les propriétés des objets n'ont alors pas d'index, elles sont toutes égales et n'ont pas besoin d'être parcourues dans un ordre déterminé. Si vous souhaitez compter les propriétés, vous devrez configurer le compteur supplémentaire (comme vous l'avez fait dans votre premier exemple).

boucle sur un tableau:

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

boucle sur un objet:

var o = {};
for (var prop in o) {
    prop // is the property name
    o[prop] // is the property value - the item
}
Bergi
la source
3
Ne faites jamais (var i=0; i<a.length; i++)comme des ressources gaspillées. Utilisation(var i=0, var len = a.length; i<len; i++)
Félix Sanz
16
@FelixSanz: Des ressources en déchets? En aucune façon. C'est une micro-optimisation prématurée qui n'est presque jamais nécessaire, et var i=0; i<a.length; i++)c'est de toute façon le modèle de boucle standard optimisé par chaque moteur javascript décent.
Bergi
3
@FelixSanz: Oui, et var i=0; i<a.length; i++c'est la meilleure pratique.
Bergi
1
KISS . Si vous écrivez des boucles là où vous en avez vraiment besoin, soit vous faites quelque chose de mal, soit vous avez un meilleur argument pour sa nécessité que la "meilleure pratique". Oui, c'est une pratique standard, mais pas pour l'optimisation générique des performances, mais uniquement pour la micro-optimisation.
Bergi
3
KISS s'applique partout. L'optimisation prématurée est une anti-pratique.
Bergi
7

Comme d'autres l'ont dit, vous ne devriez pas utiliser for..in pour parcourir un tableau.

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

Si vous voulez une syntaxe plus propre, vous pouvez utiliser forEach:

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

Si vous souhaitez utiliser cette méthode, assurez-vous d'inclure le module d'interface ES5 pour ajouter la prise en charge des anciens navigateurs.

Robert Messerle
la source
2

La réponse donnée par rushUp est correcte mais ce sera plus pratique

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

Voici une fonction eachWithIndex qui fonctionne avec tout ce qui est itérable.

Vous pouvez également écrire une fonction similaire eachWithKeyqui fonctionne avec des objets utilisant 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 bonne chose avec les générateurs est qu'ils sont paresseux et peuvent prendre comme argument le résultat d'un autre générateur.

Rivenfall
la source
1

C'est ma version d'un itérateur composite qui produit un index et la valeur de toute fonction de générateur passée avec un exemple de recherche principale (lente):

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
la source
Pourquoi avez-vous une fonction eachWithIndex[Symbol.iterator]au lieu d'une simple fonction eachWithIndex? eachWithIndexne satisfait pas l'interface itérable, ce qui est tout l'intérêt de Symbol.iterator.
Ry-
@ Ry- Bonne prise, modifiée eachWithIndexpour accepter l'itérable et renvoyer un itérable composite fermé.
akurtser du
1

En plus des très bonnes réponses que tout le monde a postées, je veux ajouter que la solution la plus performante est l'ES6 entries. Cela semble contre-intuitif pour de nombreux développeurs ici, alors j'ai créé ce benchamrk de perf .

entrez la description de l'image ici

C'est ~ 6 fois plus rapide. Principalement parce qu'il n'est pas nécessaire: a) d'accéder au tableau plus d'une fois et, b) de lancer l'index.

sospedra
la source