DEV Community

loading...
Cover image for Problems with Using for...in on Arrays in JavaScript

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

imjoshellis profile image Josh Ellis ・2 min read

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

Discussion

pic
Editor guide
Collapse
lionelrowe profile image
lionel-rowe

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 Author

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 Author

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 Author

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

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 Author

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

Collapse
sidharth74659 profile image
Sidharth

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 Author

Thanks for the extra info!

Collapse
leob profile image
leob

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 Author

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