Access to ES6 array element index inside for-of loop
Asked Answered
E

12

460

We can access array elements using a for-of loop:

for (const j of [1, 2, 3, 4, 5]) {
  console.log(j);
}

How can I modify this code to access the current index too? I want to achieve this using for-of syntax, neither forEach nor for-in.

Encrinite answered 18/12, 2015 at 5:15 Comment(0)
N
707

Use Array.prototype.keys:

for (const index of ["a", "b", "c", "d", "e"].keys()) {
  console.log(index);
}

If you want to access both the key and the value, you can use Array.prototype.entries() with destructuring:

for (const [index, value] of ["a", "b", "c", "d", "e"].entries()) {
  console.log(index, value);
}
Neiman answered 18/12, 2015 at 5:30 Comment(10)
In case someone wonders, I tested for-of with .entries() and it's twice as slow compared to .forEach(). jsperf.com/for-of-vs-foreach-with-indexWhipperin
@K48 nice to know, use "reversed for-loop" if you want to have the fastest in es: incredible-web.com/blog/…Rumple
Unfortunately, I need to yield from inside a nested loop. Can't use forEach, because the function creates scope problems for the yield keyword. But I need access to the index for my use case, so... basic old ;; loop it is, I guess.Devoir
@KyleBaker And what's wrong with a for-of loop with .entires()?Inerney
I presume it would also not be performant, but it certainly wouldn't be necessary. You can use it if you like, but the nature of a standard for loop is that one has 'access' to both the index and value in a very straightforward manner. With a for-of, one would need .entries() to have access to the index.Devoir
Instead reverse loop you may cache length jsperf.com/reverse-loop-vs-cache. For-of usefull for iterable processing when you able to process stream without creating big arrays in RAM. Loop speed wouldn't be bottleneck since you will have I/O latency in such cases.Poet
Does it work even though we're cycling an HTMLCollection?Depravity
@Depravity You can always convert HTMLCollection into array. In ES6 it is done with Array.from(myHtmlCollection), in older notations Array.prototype.slice.call( htmlCollection ). Then you can call .entries() on the array.Preraphaelite
For me, it's all about readabilty, so this wins even if the performance is not the best. For servers just buy a stronger one, and for browsers - why do you do such intensive logic on the user's side?!Lectern
@MichałPerłakowski both the entries() and keys() iterators return all indices from minimum to maximum including those that have not been set in a sparse array, so using for .. of with those won't help.Isiah
C
407

Array#entries returns the index and the value, if you need both:

for (let [index, value] of array.entries()) {

}
Cowgirl answered 18/12, 2015 at 6:33 Comment(7)
With TypeScript: 'TS2495: Type IterableIterator is not an array type or a string type'. Seems like this will be solved: github.com/Microsoft/TypeScript/pull/12346Slusher
Also Internet Explorer not supported.Libbie
Not nice. Throws an error e.g. with document.styleSheets[0].cssRules.entries() or even document.styleSheets.entries() and probably many other DOM iterable structures. Still have to use _.forEach() from lodashGrainfield
@Steven: If you don't need the index, you can just do for (var value of document.styleSheets) {}. If you do need the index you can convert the value to an array first via Array.from: for (let [index, value] of Array.from(document.styleSheets)) {}.Cowgirl
That's nice! Array.from is FTWGrainfield
Instead of complaining about which browsers support what, write in ES6 and transpile down to ES5. Write once, DRY, KISS...Stansbury
But entries() does not skip over the unset entries in a sparse array, like forEach() does.Isiah
I
137

In this world of flashy new native functions, we sometimes forget the basics.

for (let i = 0; i < arr.length; i++) {
    console.log('index:', i, 'element:', arr[i]);
}

Clean, efficient, and you can still break the loop. Bonus! You can also start from the end and go backwards with i--!

Additional note: If you're using the value a lot within the loop, you may wish to do const value = arr[i]; at the top of the loop for an easy, readable reference.

Injury answered 26/9, 2018 at 0:24 Comment(11)
Yep. Good one, clear and simple. Oh, and like that you have a super easy way to access the key/index of the array.Lyndalynde
By the way the condition should look like this -> for (let i = 0; i < arr.length; i++) without the (-1) or it will not loop throughout all the elements of the array.Lyndalynde
@Lyndalynde Good catch! I have updated my answer to reflect your note.Injury
You can still break a for-of and for (let [index, value] of array.entries()) is far easier to read. Going backwards is as easy as adding .reverse().Mouseear
This does not answer the question in any way. It would be nice in a comment (basics are always important), but not here.Orvah
I don't think this answers the particular question, but you are completely right that this is a good way to do it.Shaff
I think this is a perfectly acceptable answer to this question. It will never be the accepted answer but it has helped a few dozen people or more who have searched for this question. This is what SO is for.Decor
The simple for loop is ~8 times faster than the for of array.entries(). jsbench.me/6dkh13vqrr/1Abbyabbye
This will be highly inefficient for a sparse array.Isiah
@lawrence-witt no! for ;; is work with async await codeGiga
@KhanifIkhsanudin yep you're totally right, not sure why I commented that!Solder
N
25

In a for..of loop we can achieve this via array.entries(). array.entries returns a new Array iterator object. An iterator object knows how to access items from an iterable one at the time, while keeping track of its current position within that sequence.

When the next() method is called on the iterator key value pairs are generated. In these key value pairs the array index is the key and the array item is the value.

let arr = ['a', 'b', 'c'];
let iterator = arr.entries();
console.log(iterator.next().value); // [0, 'a']
console.log(iterator.next().value); // [1, 'b']

A for..of loop is basically a construct which consumes an iterable and loops through all elements (using an iterator under the hood). We can combine this with array.entries() in the following manner:

array = ['a', 'b', 'c'];

for (let indexValue of array.entries()) {
  console.log(indexValue);
}


// we can use array destructuring to conveniently
// store the index and value in variables
for (let [index, value] of array.entries()) {
   console.log(index, value);
}
Neutralism answered 27/9, 2018 at 22:28 Comment(0)
A
12

You can also handle index yourself if You need the index, it will not work if You need the key.

let i = 0;
for (const item of iterableItems) {
  // do something with index
  console.log(i);

  i++;
}
Address answered 27/1, 2020 at 14:18 Comment(0)
W
6

Another approach could be using Array.prototype.forEach() as

Array.from({
  length: 5
}, () => Math.floor(Math.random() * 5)).forEach((val, index) => {
  console.log(val, index)
})
Warsle answered 27/1, 2020 at 10:56 Comment(0)
I
4

in html/js context, on modern browsers, with other iterable objects than Arrays we could also use [Iterable].entries():

for(let [index, element] of document.querySelectorAll('div').entries()) {

    element.innerHTML = '#' + index

}
Internationalism answered 12/12, 2017 at 21:15 Comment(1)
Yes this works, whereas other mentioned above by @Steven Pribilinskiy other DOM methods return objects that don't have an entries method for them.Tandie
E
4

Just create a variable before the loop and assign an integer value.

let index = 0;

and then use addition assignment operator into the loop scope

index += 1;

That's It, check the below snippet example.

let index = 0;
for (const j of [1, 2, 3, 4, 5]) {
  console.log('index ',index);
  index += 1;
}
Electrostatic answered 1/4, 2021 at 10:20 Comment(0)
A
2

For those using objects that are not an Array or even array-like, you can build your own iterable easily so you can still use for of for things like localStorage which really only have a length:

function indexerator(length) {
    var output = new Object();
    var index = 0;
    output[Symbol.iterator] = function() {
        return {next:function() {
            return (index < length) ? {value:index++} : {done:true};
        }};
    };
    return output;
}

Then just feed it a number:

for (let index of indexerator(localStorage.length))
    console.log(localStorage.key(index))
Arenicolous answered 12/7, 2018 at 9:47 Comment(0)
C
2

You can try making use of indexOf menthod inside for of... loop

let arrData = [15, 64, 78]
for (const data of arrData) {
  console.log("data value", data, "index of data ", arrData.indexOf(data));
}
Crosley answered 4/7, 2022 at 4:16 Comment(1)
This works only on unique elements, and has O(n^2) complexity :(Agone
A
0

Also you can use JavaScript to solve your problem

iterate(item, index) {
    console.log(`${item} has index ${index}`);
    //Do what you want...
}

readJsonList() {    
    jsonList.forEach(this.iterate);
    //it could be any array list.
}   
Archaeozoic answered 20/10, 2020 at 23:23 Comment(0)
F
-3

es6 for...in

for(const index in [15, 64, 78]) {                        
    console.log(index);
}
Fuld answered 27/4, 2019 at 15:25 Comment(4)
The question is asking about a for...of loop not a for...inLeaper
for...in is part of the original ECMAScript specification (i.e."es1") . Also, note that for...in is meant for iterating over object properties. While it can iterate over arrays, it may not do so in the expected order. See more in the MDN documentationBiomedicine
this is a good answer for the aim of the question. Which is about "array" itteration.Rigi
no it's not a good answer, since the question was specific about for...of loop and not just general itteration...Wenoa

© 2022 - 2024 — McMap. All rights reserved.