views:

1736

answers:

5

Ok, so on a web page, I've got a JavaScript object which I'm using as an associative array. This exists statically in a script block when the page loads:

var salesWeeks = {
    "200911" : ["11 / 2009", "Fiscal 2009"],
    "200910" : ["10 / 2009", "Fiscal 2009"],
    "200909" : ["09 / 2009", "Fiscal 2009"],
    "200908" : ["08 / 2009", "Fiscal 2009"],
    "200907" : ["07 / 2009", "Fiscal 2009"],
    "200906" : ["06 / 2009", "Fiscal 2009"],
    "200905" : ["05 / 2009", "Fiscal 2009"],
    "200904" : ["04 / 2009", "Fiscal 2009"],
    "200903" : ["03 / 2009", "Fiscal 2009"],
    "200902" : ["02 / 2009", "Fiscal 2009"],
    "200901" : ["01 / 2009", "Fiscal 2009"],
    "200852" : ["52 / 2008", "Fiscal 2009"],
    "200851" : ["51 / 2008", "Fiscal 2009"]
};

The order of the key/value pairs is intentional, as I'm turning the object into an HTML select box such as this:

<select id="ddl_sw" name="ddl_sw">
<option value="">== SELECT WEEK ==</option>
<option value="200911">11 / 2009 (Fiscal 2009)</option>
<option value="200910">10 / 2009 (Fiscal 2009)</option>
<option value="200909">09 / 2009 (Fiscal 2009)</option>
<option value="200908">08 / 2009 (Fiscal 2009)</option>
<option value="200907">07 / 2009 (Fiscal 2009)</option>
<option value="200906">06 / 2009 (Fiscal 2009)</option>
<option value="200905">05 / 2009 (Fiscal 2009)</option>
<option value="200904">04 / 2009 (Fiscal 2009)</option>
<option value="200903">03 / 2009 (Fiscal 2009)</option>
<option value="200902">02 / 2009 (Fiscal 2009)</option>
<option value="200901">01 / 2009 (Fiscal 2009)</option>
<option value="200852">52 / 2008 (Fiscal 2009)</option>
<option value="200851">51 / 2008 (Fiscal 2009)</option>
</select>

...with code that looks like this (snipped from a function):

var arr = [];
arr.push(
    "<select id=\"ddl_sw\" name=\"ddl_sw\">" +
    "<option value=\"\">== SELECT WEEK ==</option>"
);

for(var key in salesWeeks)
{
    arr.push(
     "<option value=\"" + key + "\">" +
     salesWeeks[key][0] + " (" + salesWeeks[key][1] + ")" +
     "<\/option>"
    );
}

arr.push("<\/select>");

return arr.join("");

This all works fine in IE, FireFox and Opera.

However in Chrome, the order comes out all weird:

<select id="ddl_sw" name="ddl_sw">
<option value="">== SELECT WEEK ==</option>
<option value="200852">52 / 2008 (Fiscal 2009)</option>
<option value="200908">08 / 2009 (Fiscal 2009)</option>
<option value="200906">06 / 2009 (Fiscal 2009)</option>
<option value="200902">02 / 2009 (Fiscal 2009)</option>
<option value="200907">07 / 2009 (Fiscal 2009)</option>
<option value="200904">04 / 2009 (Fiscal 2009)</option>
<option value="200909">09 / 2009 (Fiscal 2009)</option>
<option value="200903">03 / 2009 (Fiscal 2009)</option>
<option value="200905">05 / 2009 (Fiscal 2009)</option>
<option value="200901">01 / 2009 (Fiscal 2009)</option>
<option value="200910">10 / 2009 (Fiscal 2009)</option>
<option value="200911">11 / 2009 (Fiscal 2009)</option>
<option value="200851">51 / 2008 (Fiscal 2009)</option>
</select>

NOTE: This order, though weird, does not change on subsequent refreshes. It's always in this order.

So, what is Chrome doing? Some optimization in how it processes the loop?

In the first place, am I wrong to rely on the order that the key/value pairs are declared in any associative array?

I never questioned it before, I just assumed the order would hold because this technique has always worked for me in the other browsers. But I suppose I've never seen it stated anywhere that the order is guaranteed. Maybe it's not?

Any insight would be awesome. Thanks.

+6  A: 

The order in which elements are stored in an associative array is not guaranteed. You'll have to sort your output explicitly.

moonshadow
+4  A: 

Think of an associative array as a paper sack into which all the key-value pairs are placed. As you reach your hand into the sack to look at the pairs (such as with the for...in loop), the order that you encounter them is for all practical purposes random.

If you want to see them in a specific order, you'll need to extract the keys into an array and sort it. Then walk across the array, using the keys you encounter to index into the associative array.

Barry Brown
Awesome, I was doing exactly that while watching this thread for a response :)I wasn't sure whether this was the case, but I had a feeling. Thanks!
Jerry
All browsers other than chrome do property iteration in the order of insertion, so you should file a bug on chrome/v8 for not doing so.
olliej
Chrome is correct; the actual spec says that order is not preserved.
Chase Seibert
Chrome probably uses a data structure that doesn't keep its elements sorted (like a hash table).
Brendan Long
@olliej: And you should read the spec, as Chase said, it says that you shouldn't rely on the order of insertion.
M28
The ES5 spec does, and i work on a JS engine -- not enumerating in insertion order breaks sites :-/
olliej
I wonder how they expect us to order the list if we can't rely on the order of insertion. I can see where having them ordered by ID is more efficient (binary search).But I still need my list of user IDs ordered by name dang it!
RandomInsano
@Barry,I would go against your thinking that chrome is correct. If the data structure in question is indeed a paper sack, then the values should be always randomized. It's not the case. Neither are they in the particular order. So it is indeed a bug in chrome.
Samnan
+2  A: 

As the other answers say, the order in which an object's properties are iterated is not defined in the spec, even though the major browsers all iterate them in the defined order.

Chrome will enumerate them in order too, if all of the object's properties contain primitive values. John Resig gives more detail on Chrome's behavior here (under "for loop order"): http://ejohn.org/blog/javascript-in-chrome/

Gabe Moothart
Good point - I was wondering whether the behavior would differ had I not been storing reference objects as the values. Thanks for the link.
Jerry
That article says"However, specification is quite different from implementation. All modern implementations of ECMAScript iterate through object properties in the order in which they were defined. Because of this the Chrome team has deemed this to be a bug and will be fixing it."I like that
Juan Mendes
+1  A: 

As pointed out Chrome's behaviour is perfectly valid.

This is a good case of where some OO thinking can help. Do you really want an associative array or just a list of objects?

var salesWeeks = [
    ["200911", "11 / 2009", "Fiscal 2009"],
    ...
]
...
for(var key in salesWeeks)
{
    arr.push(
        "<option value=\"" + salesWeeks[key][0] + "\">" +
        salesWeeks[key][1] + " (" + salesWeeks[key][2] + ")" +
        "<\/option>"
    );
}

would serve you better I think (to those that advocated some form of sort, the order is already defined - why do you want to do extra work?)

CurtainDog
+1  A: 

Contrary to all the other answers, it is CERTAINLY A BUG IN CHROME

The reason:

If the data structure is a paper sack like Barry said then:

The values should always be randimized. This is not the case in chrome

If the data structure is sequential then:

The values should be in the sequence in which they are added to the structure

While other browsers follow the second approach, chrome follows neither and there is still no explanation of what is the sorting order chrome makes up for the data structure while it is accessed.

Samnan