DEV Community

Cover image for Problems with Using for...in on Arrays in JavaScript
Josh Ellis
Josh Ellis

Posted on

Problems with Using for...in on Arrays in JavaScript

I've been doing a lot of algorithm practice lately, and I just came across a quirk of how for...in works.

I love using JavaScript's for...in and for...of loops when iterating because I consider the code much cleaner and readable at a glance.

In this post, I'll be discussing a problem you may run into with for...in if you're trying to take shortcuts like I was.

What is for...in?

In case you're not familiar, here's a simple example of code side by side that will give the same console logs:

arr = [1, 2, 3]
for (let i = 0; i < arr.length; i++) {
  console.log(arr[i]) // 1, 2, 3
}

for(let i in arr) {
  console.log(arr[i]) // 1, 2, 3
}

for(let el of arr) {
  console.log(el) // 1, 2, 3
}
Enter fullscreen mode Exit fullscreen mode

Like I said earlier, I consider the second/third to be "cleaner", but the second runs into issues if you need to use i as a number. The following won't produce the same console logs:

arr = [1, 2, 3]
for (let i = 0; i < arr.length; i++) {
  n = i + 5
  console.log(n) // 6, 7, 8
}

for(let i in arr) {
  n = i + 5
  console.log(n) // "05", "15", "25"
}
Enter fullscreen mode Exit fullscreen mode

The reason for this is because typeof i in the for...in loop is a "string" rather than a "number". This means you'll get weird results doing math on i.

Conclusion

In summary, if you need to use the numerical value for the index as you loop over an array, you either need to use the long-form verbose/explicit for loop or add something like i = parseInt(i) at the top of your loop:

arr = [1, 2, 3]
for (let i = 0; i < arr.length; i++) {
  n = i + 5
  console.log(n) // 6, 7, 8
}

for(let i in arr) {
  i = parseInt(i)
  n = i + 5
  console.log(n) // 6, 7, 8
}
Enter fullscreen mode Exit fullscreen mode

Edit: There have been a few other great suggestions on how to handle this in the comments! Check them out

Top comments (15)

Collapse
 
lionelrowe profile image
lionel-rowe • Edited

You can also use Array#keys to get an iterator with the array indexes as numbers:

const arr = [1, 2, 3]

for (const i of arr.keys()) {
    console.log(typeof i, i) // number 0, number 1, number 2
}

Similarly, Array#values gives the elements of the array, and Array#entries gives tuples of [index, element]:

for (const [idx, el] of arr.entries()) {
    console.log({ idx, el }) // {idx: 0, el: 1}, {idx: 1, el: 2}, {idx: 2, el: 3}
}
Collapse
 
imjoshellis profile image
Josh Ellis • Edited

Awesome! I didn't put 2 + 2 that .keys() and .entries() would work on arrays (I use them all the time for objects), that's great.

Collapse
 
lionelrowe profile image
lionel-rowe

It's a little different from Object.keys and so on. You can do Object.keys(arr) too, but then you'll get an array of string keys, rather than an iterator of number keys. Interestingly, there's no such thing as Object#keys β€” I guess it doesn't make sense to return an iterator of keys for something that itself isn't iterable.

Thread Thread
 
imjoshellis profile image
Josh Ellis

Right, because they're class methods on Object that take a param, that's probably why I didn't think to look into how it could work with arrays. Very interesting.

Collapse
 
imjoshellis profile image
Josh Ellis • Edited

An alternative that I didn't want to get into is using the index param from arr.forEach, but it is an option:

arr = [1, 2, 3]
arr.forEach((el, idx) => {
    n = idx + 5
    console.log(n) // 5, 6, 7
})
Collapse
 
vampiire profile image
Vamp • Edited

the reason for this is that β€œfor...in” iterates over all the enumerable (own, not proto) properties of the object. this means that the array object (subject) will have its properties (index values) iterated over.

the reason they are a string is because all object properties are stored as strings. the properties of an array are indices which are presented as strings just as any other object properties.

you can use Object(arr).entries as the iterator which will give you [index, element] pairs in β€œfor...of”. because β€œfor...of” iterates over the elements themselves which will be those entry pairs.

Collapse
 
imjoshellis profile image
Josh Ellis

Thanks for the extra details! Makes sense that is just like object properties note that you mention it

Collapse
 
srikanth74659 profile image
Srikanth

It's informative and straight to point..
Also if possible can you change examples for "What is for...in?" as

array = [4, "some text", true];

for (let i in array) {  //  in for..in 'i' returns index
  console.log(i); // 0, 1, 2
  console.log(typeof i); // string, string, string
  console.log(array[i]); // 4, some text, true
}

for (const el of array) {   //  in for..of 'el' returns element
  console.log(el);  // 4, some text, true
  console.log(typeof el);   // number, string, boolean
}

Anyways..thanks for the information

Collapse
 
imjoshellis profile image
Josh Ellis

Thanks for the extra info!

Collapse
 
leob profile image
leob • Edited

Good read ... who knows a mnemonic or aid to help remembering the difference between "for in" and "for of"? because I always forget which is which ...

Ah got it already - I think this would work:

for "I"n yields "I"ndexes
for "O"f yields "O"bjects

So "for In" yields the "I"ndexes (keys) of the elements, "for Of" yields the "O"bjects (the elements themselves) ... playing it a bit fast and loose, but you get the idea ;-)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
imjoshellis profile image
Josh Ellis

I haven’t heard of that as a way to convert to numbers. I’ll have to give it a shot when I’m at a computer later!

Collapse
 
shaerins profile image
Sharon Rachel Levin

super interesting read!

Collapse
 
jwp profile image
John Peters

I only use map, reduce and foreach.

Collapse
 
adam_cyclones profile image
Adam Crockett πŸŒ€

For of?