JavaScript Idiosyncrasies: For…in vs. for loop

I’m starting a new series, because I know there are many strange little idiosyncrasies to the JavaScript language that can be baffling to beginners and experienced programmers alike (especially programmers coming from languages like Ruby or Python).

Re-discovered one of these while practicing the Select Sort algorithm today (p.s. deliberately using nested for loops made me cringe)

Here is the problematic implementation of Select Sort, which is supposed to sort an array in place:

function selectSort(arr) { 
var smallest, oldVal;
for (var i in arr) {
smallest = i;
for (var j = i+1; j < arr.length; j++) {
if (arr[j] < arr[smallest]) smallest = j;
}
if (smallest !== i) {
oldVal = arr[i]; arr[i] = arr[smallest];
arr[smallest] = oldVal;
}
}
return arr;
}

The above code might seem like it should work, but actually, if you try to sort an array with it, unexpected things happen:

selectSort([1,2,5,7,20,-1]) // => [ -1, 2, 5, 7, 20, 1 ]

Why would this be happening? The logic may seem sound — if you walk through the iterations of the for loops, it might seem like it should sort the array.

You might now be growing suspicious of for…in — and you’re be right. Maybe you’ve been using it for years, in a language like Python (like I had). Well, there’s something weird about for…in in JavaScript. And if you take a look in the docs, low and behold:

Why would you want this behavior? Chalk it up to one of JavaScript’s many mysteries. For…in is designed for objects. Arrays are technically objects, and their indices are the keys. And when iterating over an object (in JavaScript, as in many languages), there is no guaranteed order.

This is why most people use a forEach when iterating over an array. Or, if you need to break out of the for loop before it completes, as in this case, you would want to use the long-form for loop syntax.

With this in mind, here is a re-implementation of the above select sort function:

function selectSort(arr) {
var smallest, oldVal;
for (var i = 0; i < arr.length - 1; i++) {
smallest = i;
for (var j = i+1; j < arr.length; j++) {
if (arr[j] < arr[smallest]) smallest = j;
}
if (smallest !== i) {
oldVal = arr[i];
arr[i] = arr[smallest];
arr[smallest] = oldVal;
}
}
return arr;
}

And it will return the expected results:

selectSort([1,2,5,7,20,-1]) // => [ -1, 1, 2, 5, 7, 20 ]

In short, while for…in works on Arrays, it is insidious and does not work in the expected way! Perhaps it’s safest to stay away from for…in when working with Arrays.

I have never felt so disappointed by a for loop.

Just for fun, here is the same function written in Python:

def selectSort(arr):
for i, val in enumerate(arr):
smallest = i
for j in range(i+1, len(arr)):
if arr[j] < arr[smallest]:
smallest = j
if smallest != i:
oldVal = arr[i] arr[i] = arr[smallest] arr[smallest] = oldVal
return arr

Do you have any questions or suggestions? Want to see me write about another strange idiosyncracy of JavaScript? Shoot me an email!


Originally published at paloobi.tumblr.com.

Like what you read? Give Alexandra Polubiec a round of applause.

From a quick cheer to a standing ovation, clap to show how much you enjoyed this story.