JavaScript Associative Arrays #

There seems to be some confusion regarding associative arrays in JavaScript (i.e. doing searches on the matter turns up many pages giving wrong information). First of all, these arrays (which act as hash tables) have nothing to do with the built-in Array object. They simply rely on the fact that object.property is the same as object["property"]. This means that the length property is not used, nor do any Array methods (such as join) do anything. In fact, it is better to create the associative array using the generic Object() constructor to make this clearer.

The way to iterate over the items in an associate array is to use the for (value in array) construct, allowing you to access each item's value via array[value]. It appears that the order in which properties (i.e. items) are traversed is implementation dependent. The ECMAScript specification is pretty vague on the matter, saying (in section 12.6.4) "Get name of the next property of [the object] that doesn't have the DontEnum attribute. If there is no such property, go to [the end]". Firefox, Safari and MSIE appear to traverse items in the order in which they were inserted, while KHTML (within KDE 3.1) and Opera (at least through 7.54) use a seemingly random order that presumably reflects their respective hashtable implementations.

The iteration order can be tested using a very simple code snippet such as this (click here to run it):

var items = {"dioxanes": 0,  "shunning": 1,  "plowed": 2,
            "hoodlumism": 3, "cull": 4,      "learnings": 5,
            "transmutes": 6, "cornels": 7,   "undergrowths": 8,
            "hobble": 9,     "peplumed": 10, "fluffily": 11,
            "leadoff": 12,   "dilemmas": 13, "firers": 14,
            "farmworks": 15, "anterior": 16, "flagpole": 17};
	
listString = "";
  for (var word in items)
    listString += items[word] + ", ";

alert(listString);

If the list of numbers appears in ascending order, then the browser preserves the insertion order. If you are in fact looking to traverse the object's properties in the order they were inserted in, regardless of browser implementation, you'll have to create a (possibly double) linked list that you can use to jump from object to object.

23 Comments

Hi, just wated to say thanks.
I had read all those other links from google and I was wondering why sort() and join() didn't work!

I can't believe so many people has such a false impression of associative arrays in Javascript. You are the man!

-Adeh
Problem with property id.

I am wondering why the following snippet would not work as I expect:


function OnClick(aName) {
var map = {aName : true};

for (var key in map)
alert(key + " : " + map[key]);
}
...
OnClick('a string');
...


This will assign a mapping like:
'aName' : true
instead of:
'a string' : true

Is there a solution to force Javascript to intepret aName as a string object, instead of the string itself?
When using JavaScript object literals, the property name is used as is (in fact, it doesn't need quotes in most cases). If you'd like the property name to be based on a variable, you can't use literals, you'll have to say:

map[aName] = true;
Hi Mihai,

I have been a contributor to some of the "confusion regarding associative arrays in JavaScript" in an article on my site, which was part of a series published in print. At the time of writing I had my reasons for not mentioning that associate arrays do not have to be (and in fact probably shouldn't be) Array() type objects. However I agree entirely with your comments and in the interests of accuracy and clarity I have altered my page accordingy.

Cheers,

Trevor.
just wondering if there would be a way to setup a dynamic associative array?
I would like to do the following:
var some_value=10;

var items = {"dioxanes": 0, "shunning": 1, "plowed": some_value};

I understand that the concept of associatvie array is fairly new to me, so it's maybe something simple that I'm missing.
thanks.
Thanks, that helps me a lot. :)
(^^o) <Nice info everybody! I'm currently using JavaScript Objects as hashes but is there any way to delete "hash entries" (=Object properties) by key? For example, if I do:

//----
var nantoka = new Object();
nantoka['cheese'] = 'Camembert';
nantoka['cartoon'] = 'Danger Mouse';

nantoka['cheese'] = null;
//----

Then the key/property of 'cheese' still remains (it simply equals NULL). Er...I guess what I want to do is to remove a property from an object (renaming it would also be good). Any advice/ideas?
Although citing Step 5 from the spec is interesting, I think the more compelling proof that the spec does not guarantee insertion order is the note that follows the algorithm in section 12.6.4:

The mechanics of enumerating the properties [...] is implementation dependent. The order
of enumeration is defined by the object. Properties of the object being enumerated may be deleted during enumeration. If a property that has not yet been visited during enumeration is deleted, then it will not be visited. If new properties are added to the object being enumerated during enumeration, the newly added properties are not guaranteed to be visited in the active enumeration."
Strangely, this seems broken for a key of
"toString". Any ideas?

for (var x in { toString: "pants" }) { alert( x ); }
(does not alert!)
This comment has been removed by the author.
>strangely, this seems broken for a key of
>"toString". Any ideas?
>
>for (var x in { toString: "pants" }) { alert( x ); }
>(does not alert!)

No it's not broken and you can already find the solution on this page. Have a look at kemal's post.

but just for you, here it goes.
try this:
-----------
var assoArray = {toString: "pants"};
for (var x in assoArray) {alert(assoArray[x]);}
-----------

The way I see it (and please correct me if I'm wrong, I'm just speculating here) is that the variable x has nothing to do with the associative array. It's temporaly used to hold a string. You could look at it like this.
-----------
var assoArray = {toString: "pants"};
var x = "toString";
alert( assoArray[x] );
// The for(...) merely simplifies your life by dynamically associating the key-String to a variable and let you loop through the array. So look at 'x' as a string and not a reference to an element in the array as some other languages do.
-----------
Well,

that is bad news. Basically it just means that assoc arrays don't exist in javascript.

You might wonder what the difference is with an object with properties. Well performance wise, i have always heard property lookups are slow, and array lookups are fast.

cheers for the info
"Well performance wise, i have always heard property lookups are slow, and array lookups are fast."

Well, consider the differences. A numeric array is directly linked to a memory map. Essentially, an associative array (or object) must search for its index in a table, then match it to a memory map. Whether you use an object, or you had associative arrays, you'd pretty much suffer in terms of performance there.
If performance is a big issue, use numeric arrays and keep documentation of a table match. The associative arrays just clean it up and do it for you.
Thanks, it was really useful.
Hello,
This article was very informative for me, just because I was desesperate to find something about this issue. I´m curious to know how would be the notation of a bi-dimensional associative array in javascript. Could you show us how that can be???
Thank you and congratulations.
Is it possible to do multdimensional arrays with this? i.e.:

groups= new Array(0:Array(ID:'1',name:'first users',type:'users',creatorid:'203',creationdate:'1969-12-31 18:32:49',vis:'all',active:'1'),1:Array(ID:'2',name:'2nd users',type:'users',creatorid:'203',creationdate:'1969-12-31 18:32:49',vis:'all',active:'1'));

This doesn't seem to work. What else could I try?

myk
Yes it is, if you use the syntax given in the example! ;-)

groups= {0: {ID:'1',name:'first users',type:'users',creatorid:'203',creationdate:'1969-12-31 18:32:49',vis:'all',active:'1'},1: {ID:'2',name:'2nd users',type:'users',creatorid:'203',creationdate:'1969-12-31 18:32:49',vis:'all',active:'1'}};
I know this is old, but in case anyone is wondering, as I was, Opera now enumerates by insertion order. Also, the "Proposed ECMAScript 4th Edition -- Language Overview" codifies this convention:

"Second, in ES4 the local enumeration order—the order in which properties of any one object in the
prototype chain is enumerated—is defined to be the order of insertion of those properties into that object.
Objects in the prototype chain must be visited in order, starting with the object that is the operand of the
for-in statement."
I've been one of those people who just tweaked the code until it worked, not really understanding why. This post explained a LOT!
Thanks, you have made the world a favour!
Great article, very helpful. Wondering if anyone has any tips for explicitly arranging properties in an object to follow the insertion order. Modern browsers enumerate by insertion order, but that's not enough of a guarantee - for example, Chrome doesn't always do it that way. In cases where the order is important, what do you do?
Excellent post, very helpful!

I feel so dumb now for thinking an "associative array" was an array rather than a hash map.. doh!
If you need to interate in an order, I would just create an array, that specified the order by specifying the propertyNames, and iterating through that, and looking up the value, by that propertyName, in the object....

Post a Comment