Получить счетчик/индекс цикла, используя для… синтаксиса в JavaScript

Внимание:

Вопрос по-прежнему относится к циклам for…of >. Не используйте for…in для итерации по массиву, используйте его для итерации по свойствам объекта. Тем не менее, это


Я понимаю, что основной синтаксис for…in в JavaScript выглядит следующим образом:

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

Но как мне получить счетчик/индекс цикла?

Я знаю, что мог бы сделать что-то вроде:

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

Или даже старый добрый

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

Но я бы предпочел использовать более простой цикл for-in. Я думаю, что они выглядят лучше и имеют больше смысла.

Есть ли более простой или более элегантный способ?


В Python это просто:

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

Ответ 1

for…in перебирает имена свойств, а не значения, и делает это в неопределенном порядке (да, даже после ES6). Вы не должны использовать его для перебора массивов. Для них есть метод ES5s forEach который передает и значение, и индекс в функцию, которую вы ему передаете:

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

Или ES6s Array.prototype.entries, который теперь поддерживает текущие версии браузеров:

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

Для итерируемых элементов в целом (где вы бы использовали цикл for…of а не for…in), ничего встроенного нет:

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

демонстрация

Если вы действительно имели в виду for…in перечислении свойств - вам понадобится дополнительный счетчик. Object.keys(obj).forEach может работать, но включает только собственные свойства; for…in включает перечислимые свойства в любом месте цепочки прототипов.

Ответ 2

В ES6 полезно использовать for-loop. Вы можете получить индекс для такого типа

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

Обратите внимание, что Array.entries() возвращает итератор, что позволяет ему работать в цикле for-loop; не путайте это с Object.entries(), который возвращает массив пар ключ-значение.

Ответ 3

Решение для небольших массивов массивов:

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

arr - ARRAY,   obj - КЛЮЧ текущего элемента,   i - COUNTER/INDEX

Примечание. Ключи методов() недоступны для версии IE и 9, вы должны использовать код Polyfill. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys

Ответ 4

Как насчет этого

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

Где array.forEach этого метода есть параметр index который является индексом текущего элемента, обрабатываемого в массиве.

Ответ 5

In-in-loops перебирает свойства объекта. Не используйте их для массивов, даже если они иногда работают.

Свойства объекта затем не имеют индекса, все они равны и не должны выполняться в определенном порядке. Если вы хотите подсчитать свойства, вам нужно будет настроить дополнительный счетчик (как в первом примере).

цикл над массивом:

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

цикл над объектом:

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

Ответ 6

Как говорили другие, вы не должны использовать for..in для итерации по массиву.

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

Если вам нужен более чистый синтаксис, вы можете использовать forEach:

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

Если вы хотите использовать этот метод, убедитесь, что вы включили прокладку ES5, чтобы добавить поддержку старых браузеров.

Ответ 7

Здесь функция eachWithIndex которая работает с чем угодно итеративным.

Вы также можете написать аналогичную функцию eachWithKey которая работает с объектами, использующими 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)

Хорошая вещь с генераторами - то, что они ленивы и могут взять другой результат генератора в качестве аргумента.

Ответ 8

Это моя версия составного итератора, который возвращает индекс и любое переданное значение функции генератора с примером (медленного) простого поиска:

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
}