61

Could someone explain this (strange) behavior? Why is the length in the first example 3 and not 2, and most importantly, why is the length in the second example 0? As long as the keys are numerical, length works. When they are not, length is 0. How can I get the correct length from the second example? Thank you.

a = [];
a["1"] = {"string1":"string","string2":"string"};
a["2"] = {"string1":"string","string2":"string"};
alert(a.length); // returns 3

b = [];
b["key1"] = {"string1":"string","string2":"string"};
b["key2"] = {"string1":"string","string2":"string"};
alert(b.length); // returns 0
Serenti
  • 613
  • 1
  • 5
  • 4

3 Answers3

97

One thing to note is that there is a difference between regular arrays and associative arrays. In regular arrays (real arrays), the index has to be an integer. On the other hand, associative arrays can use strings as an index. You can think of associative arrays as a map if you like. Now, also note, true arrays always start from zero. Thus in your example, you created an array in the following manner:

a = [];
a["1"] = {"string1":"string","string2":"string"};
a["2"] = {"string1":"string","string2":"string"}

Javascript was able to convert your string indexes into numbers, hence, your code above becomes:

a = [];
a[1] = {"blah"};
a[2] = {"blah"};

But remember what i said earlier: True arrays start from zero. Therefore, the javascript interpreter automatically assigned a[0] to the undefined. Try it out in either firebug or the chrome/safari console, and you will see something like this when you try to print "a". You should get something like "[undefined, Object, Object]. Hence the size 3 not 2 as you expected.

In your second example, i am pretty sure you are trying to simulate the use of an associated array, which essentially is adding properties to an object. Remember associated arrays enable you to use strings as a key. So in other terms, you are adding a property to the object. So in your example:

b["key1"] = {"string1":"string","string2":"string"};

this really means:

b.key1 = {"string1":"string","string2":"string"};

Initializing b =[] simply creates an array, but your assignment doesn't populate the array. It simply gives "b" extra properties.

starball
  • 20,030
  • 7
  • 43
  • 238
The Code Pimp
  • 2,196
  • 1
  • 17
  • 16
  • 10
    +1 For explaining a common misconception. Associative Arrays are not arrays at all, they are just complex objects – plodder Mar 27 '10 at 15:56
  • And in JS, Arrays aren't necessarily even arrays in the sense of a contiguous region of memory, where indices are offsets from the start. But those details are unnecessary. – outis Mar 27 '10 at 20:44
  • 3
    `Object.size = function(obj) { var size = 0, key; for (key in obj) { if (obj.hasOwnProperty(key)) size++; } return size; };` That will get you the actual size of the array/object. So you can just call Object.size(yourArray) and no matter where your index starts or if the keys are strings, it will give you the size. – Tom Oct 18 '12 at 05:04
  • While the starting index for JS arrays is 0, this isn't true in general. Some languages start [indexing at 1](http://stackoverflow.com/q/1499749/90527), and other languages allow the programmer to declare the starting index for each array. – outis Nov 27 '16 at 00:24
  • In many JS implementations, arrays are [sparse](http://stackoverflow.com/q/1510778/90527), so skipping indices when setting elements doesn't set the intervening elements. In the example, `a[0]` won't actually be set to `undefined`; `undefined` is what's returned for all indices that aren't set. – outis Nov 27 '16 at 00:26
  • Arrays in JS aren't "true arrays" as outlined in this answer; the difference between arrays and associative arrays/string maps is less relevant in JS as all objects are essentially associative arrays, arrays are objects, and thus arrays are associative arrays. – outis Nov 27 '16 at 00:30
19

length returns 1 + the largest integer key in the object.

In a the largest key is 2 so 1+2 is 3.

In b there are no integer keys (the keys there are key1 and key2 which cannot be converted into ints) so Javascript assumes that the largest key is -1, and 1 + -1 yields 0.

This program will help you see that:

a = [];
a["1"] = {};
a["4"] = {};
alert(a.length); // Prints 5
Itay Maman
  • 30,277
  • 10
  • 88
  • 118
  • Thank you for your answer. I guess it's a "feature" :). So, is there a way to get the amount of items in array b without having to count them manually? Or is this the only way: var len = 0; for (var name in b) len++; – Serenti Mar 27 '10 at 10:05
  • No, that's the only way. `Object` isn't really a proper associative-array is JavaScript; you can *mostly* use it as if it is, but there are pitfalls. – bobince Mar 27 '10 at 12:23
  • Object is not an Array at all. Teh confusion comes because one way to assign a property is via the a[b] syntax...but its no different from saying a.b – plodder Mar 27 '10 at 16:08
  • +1 - Very nice explanation. It makes sense about the length property returning 1 + largest because javascript's arrays are defined as "sparse". – Travis J Apr 29 '13 at 21:03
9

From the ECMAScript standard, ECMA-262, 5th ed.

15.4.5.2 length

The length property of this Array object is a data property whose value is always numerically greater than the name of every deletable property whose name is an array index.

Note the length property of an array only takes into account array indices, which are integers; setting other properties doesn't affect length.

For an array, a["3"] is equivalent to a[3] (this behavior is specified by § 15.4.5.1); 3 is an array index rather than a property. Thus setting a["3"] affects the array's length. b["key1"] is equivalent to b.key1. Setting properties don't affect the length of a collection.

Community
  • 1
  • 1
outis
  • 75,655
  • 22
  • 151
  • 221
  • Actually it's the other way around: all keys are strings, so `a[3]` is equivalent to `a["3"]`. If you say `for (i in array)`, `i` will be `"3"` and not `3`. Write `a[1000000000000000000000]= true` and you will have an array with a string key `"1e+21"`, with `length` unaffected. – bobince Mar 27 '10 at 12:22
  • @bobince: 1e21 is a different case. Since it's larger than 2**32-1 (too large to be a 32-bit int), it isn't an array index. Equivalence is symmetric, so 'a[3] is equivalent to a["3"]' says the same thing as 'a[3] is equivalent to a["3"]'. My point is that putting array indices in quotes makes no difference as to behavior, not that an element's name is an integer, as it is for other languages. My statement about automatic conversion is misleading; I'll rewrite that. – outis Mar 27 '10 at 20:19
  • @bobince: I'm trying to ignore the differences between implementation, model (as specified in the standard) and notion (how everything is to be treated in a programmer's head), which will just complicate matters. – outis Mar 27 '10 at 20:43