Get loop counter/index using for…of syntax in JavaScript
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);
}
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
}
hobbes3
Updated on February 18, 2022Comments
-
hobbes3 about 2 years
Caution:
question still applies to
for…of
loops.> Don't usefor…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 about 12 yearsOh ok. I was confused. I thought JavaScript's for-in was the same as Python's. Thanks for the clarification.
-
Felix Kling about 12 yearsActually, 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 over 9 yearsNever do
(var i=0; i<a.length; i++)
as is wasted resources. Use(var i=0, var len = a.length; i<len; i++)
-
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 over 9 yearsbest practices are best practices!
-
Bergi over 9 years@FelixSanz: Yes, and
var i=0; i<a.length; i++
is the best practise. -
Bergi over 9 yearsKISS. 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 over 9 yearsKISS is just a principle. You are not proving it applies here, i did. so i won't reply anymore. do whatever you want.
-
Bergi over 9 yearsKISS applies everywhere. Premature optimisation is an anti-practise.
-
LPing almost 9 yearsplease notice that method indexOf is NOT available for IE 8/11
-
mayankcpdixit almost 9 yearsI'd suggest: use a counter instead, increment it in loop.
-
Dean Liu over 8 yearsAdding on to mayankcpdixit, use a counter instead because indexOf could have a negative performance impact.
-
Dennis Hackethal over 8 yearsThe larger the object, the slower this will get. This does not scale.
-
Ry- over 7 yearsThis is kind of pointlessly slow and complicated because
var i = 0;
andi++;
is shorter and more efficient. Plus it doesn't work for enumerable properties that aren't own properties. -
quantumpotato over 7 yearsdon't you need to do var i = 0, because lets are unchanging?
-
Ry- over 7 years@quantumpotato:
let
s arevar
s with block scope.const
s are unchanging. -
Braden Best almost 7 yearsI 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 thatmyArray.indexOf(value)
garbage I've been doing. +1. -
trusktr almost 7 yearsThis is a much better answer than the accepted one!
-
trusktr almost 7 years@mayankcpdixit Incrementing a counter won't work in for..of loops that modify the array during iteration.
-
trusktr almost 7 years@Ryan
var i = 0;
andi++;
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- almost 7 years@trusktr: Why are you replying to a comment about this answer, which iterates over an array in ϴ(n²) time?
-
mayankcpdixit almost 7 years@trusktr yes! but
for...of
is still relatively new. If you're usingfor...in
, counter would be a good coice. -
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- 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 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- 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 callindexOf
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 almost 7 yearsI 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 almost 7 years
entries()
is returning an empty object:{}
. Any idea why that would be? Myarray
is an Array of Objects. -
tonyg over 6 years@JoshuaPinter try
Object.entries(array)
instead ofarray.entries()
-
Shog9 over 6 yearsIt'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 callingnext()
, which for-of does behind the scenes. cc @tonyg -
tonyg over 6 yearswoah, I take it back. I was using a repl and
array.entries()
always returns{}
but if you actually use the code above, usingfor ... of array.entries()
it works just fine. Sorry about that. -
Joshua Pinter over 6 yearsOh, 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 about 6 yearsThis is great and a far more elegant solution in 2018. Thanks
-
Ade about 6 yearsIn the example above, 'i' which should be the index returns undefined, please help.
-
Ry- about 6 years@Ade In which example?
-
Ade about 6 yearsI 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 over 5 yearsThis 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- 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 over 5 yearsGood idea. I will edit the title to save countless others coming here from Google in hope of finding a different discussion
-
Dheeraj Bhaskar over 5 yearsthis was a detailed answer, thanks for it. Really clarified all the things discussed
-
Ry- about 5 yearsWhy do you have a function
eachWithIndex[Symbol.iterator]
instead of just a functioneachWithIndex
?eachWithIndex
doesn’t satisfy the iterable interface, which is the whole point ofSymbol.iterator
. -
akurtser about 5 years@Ry- Good catch, changed
eachWithIndex
to accept iterable and return a closured composite iterable. -
Deiv about 5 yearsThe chosen answer was posted 6 years before this one and has the same thing already in it...
-
klewis over 4 yearsstupid question but what does %d and %s actually stand for, or could they be any letter I want them to be?
-
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 over 4 yearsNote the other answer below in reference to arrow functions... But the above does work too.
-
smartworld-dm over 4 yearsForeach is not good for optimization, since
break
is not available. -
Jayson Minard about 4 yearsThis has horrible performance characteristics, and should not be suggested ever.
-
Congelli501 almost 4 yearsAnd it won't work it some value are duplicated in the array
-
ABGR almost 4 yearsPlease 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 almost 4 yearsI'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 almost 4 yearsuseful with iterable objects, thanks pal, my_list.indexOf(element), though lambda expressions forEach is very useful.
-
WestCoastProjects over 3 years@Marshal Your link is dead
-
Marshal over 3 years@javadba, That's because jsperf is down. I'll create a new answer
-
Ry- over 3 years
|| []
is unnecessary and will never be used;array.entries()
is always truthy. -
Ry- over 3 yearsThis is unnecessarily quadratic and will produce incorrect results when the array contains duplicates.
-
Ry- over 3 yearsSaying 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 over 3 yearsAlso this allows
await
to work in sequence, whereasforEach
does not. -
Barbz_YHOOL almost 3 years[index, val] never works for me, it says "undefined"
-
Renish Gotecha almost 3 yearscan you share your array ?
-
user1338062 almost 3 yearsThe downside of
forEach
is thatawait
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 about 2 yearsBe aware this only works for
Array
.for...of
works on anyIterable
. You might have to convert toArray
withfor (const [index, value] of [...array].entries())
. Iterables includeHTMLCollection
NodeList
, and other DOM classes. -
General Grievance almost 2 years
ReferenceError: menu is not defined