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

740,525

Solution 1

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.

Solution 2

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.

Solution 3

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.

Solution 4

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

Solution 5

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
}
Share:
740,525
hobbes3
Author by

hobbes3

Updated on February 18, 2022

Comments

  • hobbes3
    hobbes3 about 2 years

    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
    
  • hobbes3
    hobbes3 about 12 years
    Oh ok. I was confused. I thought JavaScript's for-in was the same as Python's. Thanks for the clarification.
  • Felix Kling
    Felix Kling about 12 years
    Actually, obj will be the array index, but there is no guarantee that it is in order and that it won't include other property names.
  • Félix Sanz
    Félix Sanz over 9 years
    Never do (var i=0; i<a.length; i++) as is wasted resources. Use (var i=0, var len = a.length; i<len; i++)
  • Bergi
    Bergi over 9 years
    @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.
  • Félix Sanz
    Félix Sanz over 9 years
    best practices are best practices!
  • Bergi
    Bergi over 9 years
    @FelixSanz: Yes, and var i=0; i<a.length; i++ is the best practise.
  • Bergi
    Bergi over 9 years
    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.
  • Félix Sanz
    Félix Sanz over 9 years
    KISS is just a principle. You are not proving it applies here, i did. so i won't reply anymore. do whatever you want.
  • Bergi
    Bergi over 9 years
    KISS applies everywhere. Premature optimisation is an anti-practise.
  • LPing
    LPing almost 9 years
    please notice that method indexOf is NOT available for IE 8/11
  • mayankcpdixit
    mayankcpdixit almost 9 years
    I'd suggest: use a counter instead, increment it in loop.
  • Dean Liu
    Dean Liu over 8 years
    Adding on to mayankcpdixit, use a counter instead because indexOf could have a negative performance impact.
  • Dennis Hackethal
    Dennis Hackethal over 8 years
    The larger the object, the slower this will get. This does not scale.
  • Ry-
    Ry- over 7 years
    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.
  • quantumpotato
    quantumpotato over 7 years
    don't you need to do var i = 0, because lets are unchanging?
  • Ry-
    Ry- over 7 years
    @quantumpotato: lets are vars with block scope. consts are unchanging.
  • Braden Best
    Braden Best almost 7 years
    I did not know this was possible: myArray.forEach(function (value, *i*) (emphasis mine). This is perfect. I knew there had to be a better way than that myArray.indexOf(value) garbage I've been doing. +1.
  • trusktr
    trusktr almost 7 years
    This is a much better answer than the accepted one!
  • trusktr
    trusktr almost 7 years
    @mayankcpdixit Incrementing a counter won't work in for..of loops that modify the array during iteration.
  • trusktr
    trusktr almost 7 years
    @Ryan var i = 0; and i++; won't work in loops that modify the array during iteration. @rushUp's answer is superb, because the index will be the original index of each item before iteration, regardless of the array being modified during iteration.
  • Ry-
    Ry- almost 7 years
    @trusktr: Why are you replying to a comment about this answer, which iterates over an array in ϴ(n²) time?
  • mayankcpdixit
    mayankcpdixit almost 7 years
    @trusktr yes! but for...of is still relatively new. If you're using for...in, counter would be a good coice.
  • trusktr
    trusktr almost 7 years
    @Ryan If modifying the collection during iteration isn't required, then yeah, a regular for loop is a good way to make it faster. :}
  • Ry-
    Ry- almost 7 years
    @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.
  • trusktr
    trusktr almost 7 years
    @Ryan That makes code harder to read. I would say it depends on the use case. If you are using it for something that the end user will not perceive performance-wise, then use it. For 60fps graphics? Maybe not.
  • Ry-
    Ry- almost 7 years
    @trusktr: Which option to pick depends on the use case. Whether to pick this option does not. There is no reason to get a new Object.keys and call indexOf on it for each item, ever. It takes time proportional to n² instead of n and is even less readable than a regular explicit loop.
  • u8y7541
    u8y7541 almost 7 years
    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.
  • Joshua Pinter
    Joshua Pinter almost 7 years
    entries() is returning an empty object: {}. Any idea why that would be? My array is an Array of Objects.
  • tonyg
    tonyg over 6 years
    @JoshuaPinter try Object.entries(array) instead of array.entries()
  • Shog9
    Shog9 over 6 years
    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
  • tonyg
    tonyg over 6 years
    woah, I take it back. I was using a repl and array.entries() always returns {} but if you actually use the code above, using for ... of array.entries() it works just fine. Sorry about that.
  • Joshua Pinter
    Joshua Pinter over 6 years
    Oh, very cool! Thanks for the explanation. I ended up using a different method but I'll take a look at this again now that I know that. Thanks for updating the answer as well.
  • mmenschig
    mmenschig about 6 years
    This is great and a far more elegant solution in 2018. Thanks
  • Ade
    Ade about 6 years
    In the example above, 'i' which should be the index returns undefined, please help.
  • Ry-
    Ry- about 6 years
    @Ade In which example?
  • Ade
    Ade about 6 years
    I have sorted my self now. (The mistake I'd made was following your solution whilst still using the ECMA arrow and without the keyword, "function". Thanks Ry.
  • vsync
    vsync over 5 years
    This does not answer the question's title, and it's more important to help the thousands getting here from Google than helping the OP (which mistakenly iterated an Array instead of Object)
  • Ry-
    Ry- over 5 years
    @vsync: It does answer the question’s title. See the last paragraph. Also, feel free to edit the title to reflect the question.
  • vsync
    vsync over 5 years
    Good idea. I will edit the title to save countless others coming here from Google in hope of finding a different discussion
  • Dheeraj Bhaskar
    Dheeraj Bhaskar over 5 years
    this was a detailed answer, thanks for it. Really clarified all the things discussed
  • Ry-
    Ry- about 5 years
    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.
  • akurtser
    akurtser about 5 years
    @Ry- Good catch, changed eachWithIndex to accept iterable and return a closured composite iterable.
  • Deiv
    Deiv about 5 years
    The chosen answer was posted 6 years before this one and has the same thing already in it...
  • klewis
    klewis over 4 years
    stupid question but what does %d and %s actually stand for, or could they be any letter I want them to be?
  • Ry-
    Ry- over 4 years
    @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.
  • T.Woody
    T.Woody over 4 years
    Note the other answer below in reference to arrow functions... But the above does work too.
  • smartworld-dm
    smartworld-dm over 4 years
    Foreach is not good for optimization, since break is not available.
  • Jayson Minard
    Jayson Minard about 4 years
    This has horrible performance characteristics, and should not be suggested ever.
  • Congelli501
    Congelli501 almost 4 years
    And it won't work it some value are duplicated in the array
  • ABGR
    ABGR almost 4 years
    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)
  • Marshal
    Marshal almost 4 years
    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
  • christianbueno.1
    christianbueno.1 almost 4 years
    useful with iterable objects, thanks pal, my_list.indexOf(element), though lambda expressions forEach is very useful.
  • WestCoastProjects
    WestCoastProjects over 3 years
    @Marshal Your link is dead
  • Marshal
    Marshal over 3 years
    @javadba, That's because jsperf is down. I'll create a new answer
  • Ry-
    Ry- over 3 years
    || [] is unnecessary and will never be used; array.entries() is always truthy.
  • Ry-
    Ry- over 3 years
    This is unnecessarily quadratic and will produce incorrect results when the array contains duplicates.
  • Ry-
    Ry- over 3 years
    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?
  • geoidesic
    geoidesic over 3 years
    Also this allows await to work in sequence, whereas forEach does not.
  • Barbz_YHOOL
    Barbz_YHOOL almost 3 years
    [index, val] never works for me, it says "undefined"
  • Renish Gotecha
    Renish Gotecha almost 3 years
    can you share your array ?
  • user1338062
    user1338062 almost 3 years
    The downside of forEach is that await inside is scoped to the function parameter, not the outer scope. So if you want to await inside the loop, you probably want to use .entries().
  • ShortFuse
    ShortFuse about 2 years
    Be aware this only works for Array. for...of works on any Iterable. You might have to convert to Array with for (const [index, value] of [...array].entries()). Iterables include HTMLCollection NodeList, and other DOM classes.
  • General Grievance
    General Grievance almost 2 years
    ReferenceError: menu is not defined