640

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
Ry-
  • 218,210
  • 55
  • 464
  • 476
hobbes3
  • 28,078
  • 24
  • 87
  • 116

12 Answers12

1035

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.

Ry-
  • 218,210
  • 55
  • 464
  • 476
  • 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. – Felix Kling Apr 16 '12 at 18:52
  • don't you need to do var i = 0, because lets are unchanging? – quantumpotato Dec 13 '16 at 19:47
  • 3
    @quantumpotato: `let`s are `var`s with block scope. `const`s are unchanging. – Ry- Dec 14 '16 at 07:50
  • 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. – Braden Best May 12 '17 at 07:11
  • In the example above, 'i' which should be the index returns undefined, please help. – Ade Apr 05 '18 at 11:02
  • @Ade In which example? – Ry- Apr 05 '18 at 17:19
  • 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. – Ade Apr 06 '18 at 05:35
  • 2
    stupid question but what does %d and %s actually stand for, or could they be any letter I want them to be? – klewis Aug 07 '19 at 13:24
  • 2
    @klewis: `%d` formats an integer and `%s` formats a string. They’re inspired by [printf](https://en.wikipedia.org/wiki/Printf_format_string). A spec is in progress at https://console.spec.whatwg.org/#formatter. – Ry- Aug 07 '19 at 13:39
  • Note the other answer below in reference to arrow functions... But the above does work too. – T.Woody Nov 24 '19 at 03:20
  • 2
    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()`. – user1338062 Jun 12 '21 at 07:33
  • Which one is faster in Node 18? – Antoine Apr 13 '23 at 11:27
  • @Antoine: It’s usually close enough not to matter in modern V8; I essentially always use `for of`. You can benchmark your own use case to get concrete numbers. – Ry- Apr 13 '23 at 19:08
  • Note: you should take special precautions if you're using an async callback with something like foreach since it would run the loops in parallel if you were hoping to run in serial. – CTS_AE Jun 23 '23 at 22:07
  • From MDN: "The traversal order, as of modern ECMAScript specification, is well-defined and consistent across implementations. Within each component of the prototype chain, all non-negative integer keys (those that can be array indices) will be traversed first in ascending order by value, then other string keys in ascending chronological order of property creation." https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...in#description – merlindru Aug 05 '23 at 10:04
434

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.

mikemaccana
  • 110,530
  • 99
  • 389
  • 494
rushUp
  • 4,391
  • 1
  • 8
  • 11
  • 4
    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. – u8y7541 Jul 29 '17 at 17:31
  • 1
    `entries()` is returning an empty object: `{}`. Any idea why that would be? My `array` is an Array of Objects. – Joshua Pinter Jul 30 '17 at 22:57
  • @JoshuaPinter try `Object.entries(array)` instead of `array.entries()` – tonyg Sep 29 '17 at 15:50
  • 2
    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 – Shog9 Sep 29 '17 at 16:30
  • 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. – tonyg Sep 29 '17 at 17:03
  • 5
    Also this allows `await` to work in sequence, whereas `forEach` does not. – geoidesic Dec 06 '20 at 13:08
  • 1
    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. – ShortFuse Feb 09 '22 at 00:52
  • nicely done! ... – KD.S.T. Oct 26 '22 at 17:33
  • 1
    Why use `let` instead of `const`? – Ben Nov 11 '22 at 15:39
  • I agree with @Ben. The values don't change in scope so should be `const`, not `let`. – webdevguy Apr 18 '23 at 20:30
53

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.

Sanjay Shr
  • 2,026
  • 2
  • 16
  • 17
21

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

  • 8
    I'd suggest: use a counter instead, increment it in loop. – mayankcpdixit Jul 06 '15 at 01:43
  • 4
    Adding on to mayankcpdixit, use a counter instead because indexOf could have a negative performance impact. – Dean Liu Dec 13 '15 at 01:14
  • 3
    The larger the object, the slower this will get. This does not scale. – Dennis Hackethal Jan 13 '16 at 22:03
  • 3
    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. – Ry- Aug 12 '16 at 06:52
  • @mayankcpdixit Incrementing a counter won't work in for..of loops that modify the array during iteration. – trusktr Jun 28 '17 at 01:24
  • @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. – trusktr Jun 28 '17 at 01:25
  • @trusktr: Why are you replying to a comment about this answer, which iterates over an array in ϴ(n²) time? – Ry- Jun 28 '17 at 04:11
  • @trusktr yes! but `for...of` is still relatively new. If you're using `for...in`, _counter_ would be a good coice. – mayankcpdixit Jul 04 '17 at 10:43
  • @Ryan If modifying the collection during iteration isn't required, then yeah, a regular for loop is a good way to make it faster. :} – trusktr Jul 19 '17 at 22:31
  • 1
    @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. – Ry- Jul 19 '17 at 22:33
  • @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. – trusktr Jul 19 '17 at 22:45
  • @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. – Ry- Jul 19 '17 at 23:33
  • 1
    This has horrible performance characteristics, and should not be suggested ever. – Jayson Minard Feb 17 '20 at 23:53
  • 1
    And it won't work it some value are duplicated in the array – Congelli501 Jun 03 '20 at 19:46
  • @JaysonMinard To be fair, the author did specify "small arrays". Performance is usually not a concern there. – TylerH Aug 30 '22 at 13:22
  • 1
    why learn the non performant way ever when the better options are just as easy? this is how people learn bad habits and don't know they are bad habits. – Jayson Minard Sep 04 '22 at 13:52
16

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
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • 3
    Never do `(var i=0; i – Félix Sanz Aug 29 '14 at 01:59
  • 27
    @FelixSanz: Waste resources? No way. That is a premature micro-optimisation that is hardly ever necessary, and `var i=0; i – Bergi Aug 29 '14 at 10:57
  • best practices are best practices! – Félix Sanz Aug 29 '14 at 18:10
  • 5
    @FelixSanz: Yes, and `var i=0; i – Bergi Aug 30 '14 at 11:01
  • 1
    [KISS](http://en.wikipedia.org/wiki/KISS_principle). 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. – Bergi Sep 01 '14 at 12:02
  • KISS is just a principle. You are not proving it applies here, i did. so i won't reply anymore. do whatever you want. – Félix Sanz Sep 01 '14 at 14:55
  • 4
    KISS applies everywhere. [Premature optimisation](https://en.wikipedia.org/wiki/Premature_optimisation) is an anti-practise. – Bergi Sep 01 '14 at 15:01
11

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.

Robert Messerle
  • 3,022
  • 14
  • 18
8

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

for (let [index, val] of array.entries() || []) {
   // your code goes here    
}
Renish Gotecha
  • 2,232
  • 22
  • 21
3

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.

enter image description here

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

sospedra
  • 14,238
  • 3
  • 21
  • 32
  • 2
    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](https://jsperf.com/enumerate/5) – Marshal Jul 30 '20 at 10:41
  • @Marshal Your link is dead – WestCoastProjects Oct 13 '20 at 13:49
  • @javadba, That's because [jsperf is down](https://stackoverflow.com/questions/37695890/how-to-profile-javascript-now-that-jsperf-is-down). I'll create a new answer – Marshal Oct 15 '20 at 06:32
  • 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? – Ry- Oct 31 '20 at 00:23
2

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.

Rivenfall
  • 1,189
  • 10
  • 15
1

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 iterable) {
        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
}
vaughan
  • 6,982
  • 6
  • 47
  • 63
akurtser
  • 762
  • 6
  • 11
  • 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- Apr 10 '19 at 11:50
  • @Ry- Good catch, changed `eachWithIndex` to accept iterable and return a closured composite iterable. – akurtser Apr 11 '19 at 14:00
-1
// this loop is used in advanced javascript
//For Example I have an array:
let array = [1,2,3,4,5];
1) for(let key in array){
      console.log(key);//this shows index of array {Result: 0,1,2,3,4}
      console.log(array[key]);//this show values of array {Result: 1,2,3,4,5}
   }
//Hopefully, You will quickly understand;
-3

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) so this should be better suited for Sets than for Arrays.

assayag.org
  • 709
  • 10
  • 24
  • 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) – ABGR Jun 23 '20 at 08:16
  • 1
    useful with iterable objects, thanks pal, my_list.indexOf(element), though lambda expressions forEach is very useful. – christianbueno.1 Jul 31 '20 at 14:43
  • 12
    This is unnecessarily quadratic and will produce incorrect results when the array contains duplicates. – Ry- Oct 31 '20 at 00:21