ChatGPT解决这个技术问题 Extra ChatGPT

Get loop counter/index using for…of syntax in JavaScript

Caution: question still applies to for…of loops.> Don't use for…in to iterate over an Array, use it to iterate over the properties of an object. That said, this

I understand that the basic for…in syntax in JavaScript looks like this:

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

But how do I get the loop counter/index?

I know I could probably do something like:

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

Or even the good old:

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

But I would rather use the simpler for-in loop. I think they look better and make more sense.

Is there a simpler or more elegant way?

In Python it's easy:

for i, obj in enumerate(myArray):
    print i
Don't use for...in for arrays. And anyways, it iterates over the property names, not the values of the properties.
It's an array, not an object, right? So, alert(obj)?

R
Ry-

for…in iterates over property names, not values, and does so in an unspecified order (yes, even after ES6). You shouldn’t use it to iterate over arrays. For them, there’s ES5’s forEach method that passes both the value and the index to the function you give it:

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

Or ES6’s Array.prototype.entries, which now has support across current browser versions:

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

For iterables in general (where you would use a for…of loop rather than a for…in), there’s nothing built-in, however:

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

If you actually did mean for…in – enumerating properties – you would need an additional counter. Object.keys(obj).forEach could work, but it only includes own properties; for…in includes enumerable properties anywhere on the prototype chain.


Oh ok. I was confused. I thought JavaScript's for-in was the same as Python's. Thanks for the clarification.
@quantumpotato: lets are vars with block scope. consts are unchanging.
this was a detailed answer, thanks for it. Really clarified all the things discussed
stupid question but what does %d and %s actually stand for, or could they be any letter I want them to be?
@klewis: %d formats an integer and %s formats a string. They’re based on printf. A spec is in progress at console.spec.whatwg.org/#formatter.
m
mikemaccana

In ES6, it is good to use a for... of loop. You can get index in for... of like this

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

Note that Array.entries() returns an iterator, which is what allows it to work in the for-of loop; don't confuse this with Object.entries(), which returns an array of key-value pairs.


This is a much better answer than the accepted one!
I think this solution is better than the forEach one... It uses the nomral for...of loop syntax, and you don't have to use a separate function. In other words, it's syntactically better. The OP seems to have wanted this.
entries() is returning an empty object: {}. Any idea why that would be? My array is an Array of Objects.
It's supposed to do that, Joshua - the object is an iterator, an object with a next() method that will return subsequent entries in the array each time it is called. There's no (visible) data in it; you get the data in the underlying object by calling next(), which for-of does behind the scenes. cc @tonyg
Also this allows await to work in sequence, whereas forEach does not.
S
Sanjay Shr

How about this

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

Where array.forEach this method has an index parameter which is the index of the current element being processed in the array.


The chosen answer was posted 6 years before this one and has the same thing already in it...
Foreach is not good for optimization, since break is not available.
佚名

Solution for small array collections:

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

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

Notice: Method keys() is not available for IE version <9, you should use Polyfill code. https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Object/keys


I'd suggest: use a counter instead, increment it in loop.
Adding on to mayankcpdixit, use a counter instead because indexOf could have a negative performance impact.
The larger the object, the slower this will get. This does not scale.
This is kind of pointlessly slow and complicated because var i = 0; and i++; is shorter and more efficient. Plus it doesn't work for enumerable properties that aren't own properties.
@trusktr: And if it is required… you should still not use this. Just alter the counter when you alter the collection. If it doesn’t have to be in-place, do a nice functional transformation instead.
B
Bergi

For-in-loops iterate over properties of an Object. Don't use them for Arrays, even if they sometimes work.

Object properties then have no index, they are all equal and not required to be run through in a determined order. If you want to count properties, you will have to set up the extra counter (as you did in your first example).

loop over an Array:

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

loop over an Object:

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

Never do (var i=0; i<a.length; i++) as is wasted resources. Use (var i=0, var len = a.length; i<len; i++)
@FelixSanz: Waste resources? No way. That is a premature micro-optimisation that is hardly ever necessary, and var i=0; i<a.length; i++) is the standard loop pattern that is optimised by every decent javascript engine anyway.
@FelixSanz: Yes, and var i=0; i<a.length; i++ is the best practise.
KISS. If you write loops where you really need this you either are doing something wrong, or you have a better argument for its necessity than "best practise". Yes, it is a standard practise, but not for generic performance optimisation, but only for micro-optimisation.
KISS applies everywhere. Premature optimisation is an anti-practise.
R
Robert Messerle

As others have said, you shouldn't be using for..in to iterate over an array.

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

If you want cleaner syntax, you could use forEach:

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

If you want to use this method, make sure that you include the ES5 shim to add support for older browsers.


R
Renish Gotecha

Answer Given by rushUp Is correct but this will be more convenient

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

|| [] is unnecessary and will never be used; array.entries() is always truthy.
[index, val] never works for me, it says "undefined"
can you share your array ?
a
assayag.org

To use for..of loop on array and retrieve index you can you use array1.indexOf(element) which will return the index value of an element in the loop. You can return both the index and the value using this method.

array1 = ['a', 'b', 'c'] for (element of array1) { console.log(array1.indexOf(element), element) // 0 a 1 b 2 c }

As mentionned in comments, this will return false index when the array contains non uniques values. (considering arr = ['a', 'b', 'c', 'a'], index of arr[3] will return 0 instead of 3)


Please explain your code a little as to what it does rather than adding snippets only. Plus it doesn't exactly answer the question. The question is rather about objects and taking an example of an array would perhaps be oversimplification. (From Review)
useful with iterable objects, thanks pal, my_list.indexOf(element), though lambda expressions forEach is very useful.
This is unnecessarily quadratic and will produce incorrect results when the array contains duplicates.
R
Rivenfall

Here's a function eachWithIndex that works with anything iterable.

You could also write a similar function eachWithKey that works with objets using 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)

The good thing with generators is that they are lazy and can take another generator's result as an argument.


s
sospedra

On top of the very good answers everyone posted I want to add that the most performant solution is the ES6 entries. It seems contraintuitive for many devs here, so I created this perf benchamrk.

https://i.stack.imgur.com/2WAkC.png

It's ~6 times faster. Mainly because doesn't need to: a) access the array more than once and, b) cast the index.


I've got to say you are not comparing apple to apple in the above test case. In classic, extra const v is defined plus the unnecessary type conversion Number(i) all led to its overhead. By removing those bits, my result shows the contrary: classic is 4 times faster. Please check the updated version here
@Marshal Your link is dead
@javadba, That's because jsperf is down. I'll create a new answer
Saying it’s the “most performant solution” based on a benchmark that only includes one other approach (that also happens to be wrong) is pretty misleading. How about comparing it against the top answers?
a
akurtser

That's my version of a composite iterator that yields an index and any passed generator function's value with an example of (slow) prime search:

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 }


Why do you have a function eachWithIndex[Symbol.iterator] instead of just a function eachWithIndex? eachWithIndex doesn’t satisfy the iterable interface, which is the whole point of Symbol.iterator.
@Ry- Good catch, changed eachWithIndex to accept iterable and return a closured composite iterable.
佚名

By this for of loop system in js, you can find all arrays elements with index no ...

const menus = ['Bengali', 'Italiano', 'Americano', 'Thai', 'Chinese'];

for (const menus of menu.entries()) {
console.log(menus);
}

ReferenceError: menu is not defined