33

I'm not sure if this is normal behavior, but running this:

for (var i in [1, 2, 3]) {
    console.log(i + 1);
}

Results in this:

// 01
// 11
// 21

Could somebody please explain, why is var i being treated like a string in this situation and not if I do for (var i = 0; i < [1, 2, 3].length; i++)?

abfarid
  • 555
  • 6
  • 13
  • 3
    If you are using a platform that supports [a suitable subset of ES6](http://kangax.github.io/compat-table/es6/#test-for..of_loops), a for-of loop will give you the behavior you want: `for (let i of [1, 2, 3]) { console.log(i + 1); }` – senshin Aug 17 '16 at 06:50
  • 1
    for (var i of [1, 2, 3]) { console.log(typeof i); console.log(typeof (i + 1)); } for (let i of [1, 2, 3]) { console.log(typeof i); console.log(typeof (i + 1)); } Both are resulting in number type. But remember in js everything treated as object and key objects are usually strings. – Prasad Shinde Aug 17 '16 at 08:28
  • 4
    Just when you think you've seen all the weird JavaScript behaviours, something like this comes up. – Karl Nicoll Aug 17 '16 at 10:53
  • 1
    [Why is using a `for ... in` loop for JavaScript arrays a bad idea?](http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea) – Phylogenesis Aug 17 '16 at 12:33
  • [Is JavaScript array index a string or an integer?](http://stackoverflow.com/q/27537677/1048572) – Bergi Aug 17 '16 at 17:53

8 Answers8

38

Its most likely because in this for loop style (for..in), it is treating i as a key, and since keys in objects are usually strings (yes, an array is a type of object in javascript), it is treating it as a String.

parseInt(i) works in this situation, but for good programming practice, you would want to use a for loop that looks similar to this:

var array = [1, 2, 3];
for (var i = array.length - 1; i >= 0; i--) {
    // do work with each array element here
} 
Derek Pollard
  • 6,953
  • 6
  • 39
  • 59
  • 6
    Alternatively, if the code you've submitted is not just a boiled down example to illustrate a bigger issue, use the old fashioned `for(var i = 0; i < 3; i++){}` – Matias Faure Aug 16 '16 at 22:34
  • 2
    While the end result is what the OP wanted, I would call this a bad programming practice. – Xorifelse Aug 17 '16 at 16:54
  • 2
    You absolutely should not use `parseInt` here. Rather use a proper loop. – Bergi Aug 17 '16 at 17:53
  • I addressed the question, and the solution fits. I could provide a proper loop if you guys would like. – Derek Pollard Aug 17 '16 at 21:24
  • 1
    @You: Thanks, much better, though looping backwards is quite unidiomatic – Bergi Aug 18 '16 at 20:09
  • @Bergi you know what, I think I might make a new question for that, it has to do with the editor I use....actually, I just found my answer as to *why* this type of loop is better: http://stackoverflow.com/a/17484696/2020002 – Derek Pollard Aug 18 '16 at 20:17
  • 1
    @You It's not "better". You should favour readability over microoptimisations. – Bergi Aug 18 '16 at 20:25
18

The reason is that for .. in iterates object properties ("keys"), even for arrays. The "keys" in this case are the "indexes" of the array but it's still just object keys. As all object keys are strings, i gets a string value.

Amit
  • 45,440
  • 9
  • 78
  • 110
12

I think you want the values of the array, not the keys. If you can't use ES6, Xorifelse's answer works, but if you can, there is for ... of that works exactly as you probably thought:

for (let i of [1, 2, 3]) {
  console.log(i + 1);
}

There is also Array.prototype.forEach in ES5 and up:

[1, 2, 3].forEach(function(value, index) {
  console.log(value + 1);
});
Community
  • 1
  • 1
D_4_ni
  • 901
  • 6
  • 18
10

Using the in keyword you're looping over each key in the object and those are string types and when using the + operator on a string will cause it to do a concatenation assignment.

If you want to perform an arithmetic assignment both the left and the right value of the operator have to be an integer type. A solution would be to try to parse the string to an integer:

console.log(parseInt("0") + 1); // 1

However...
You should be looping over the value instead:

var data = [1, 2, 3];

for(var key in data){
  console.log('a: data['+ key + '] = ' + data[key]);
}

for (var i = 0; i < data.length; i++) {
  console.log('b: data['+ i + '] = ' + data[i]);
}

data.forEach(function(value, index){
  console.log('c: data[' + index + '] = ' + value);
});

You could use the ES6 method to loop over the value alone:

for(let v of [1, 2, 3]) {
  console.log(v);
}

The drawback of this method is the incompatibility of older android devices since its a somewhat new standard.


If you're using jQuery, you can also take a look at $.each as it allows for a key, value iteration in a nice one-liner that is compatible with older devices.

$.each([1, 2, 3], function(index, value) {
  console.log( index + ": " + value );
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Xorifelse
  • 7,878
  • 1
  • 27
  • 38
5

It's because i is a key/ index of every element, and it's string type. So you are in fact concatenating string and int – the result is string.

jedrzejginter
  • 402
  • 3
  • 10
4

That is because with the "in" keyword you traverse the keys of the object, not array indexes. However in this case, since the object is an array, the keys of the items in are indexes. And last, the keys in a for..in loop are strings.

So if you need numeric indexes of an array, you need to use a regular for loop.

var array = [0, 1, 2];
for (var i = 0; i < array.length; i++) {
  console.log(i + 1);
}
Pedro Otero
  • 324
  • 5
  • 15
3

Well when you use the for...in loop you're visiting each key from an object. Arrays are objects and their keys are numbers, but usually object keys are treated like strings.

If you want to convert the key to int, you can use parseInt function. link!

-5

for (var i in [1, 2, 3]) {
    console.log(parseInt(i) + parseInt(1));
}