views:

1283

answers:

5

can an array in JS be associative AND indexed? I'd like to be able to lookup an item in the array by its position or a key value.. possible?

+1  A: 

The order in which objects appear in an associative javascript array is not defined, and will differ across different implementations. For that reason you can't really count on a given associative key to always be at the same index.

EDIT:

as Perspx points out, there aren't really true associative arrays in javascript. The statement foo["bar"] is just syntactic sugar for foo.bar

If you trust the browser to maintain the order of elements in an object, you could write a function

function valueForIndex(obj, index) {

    var i = 0;
    for (var key in obj) {

        if (i++ == index)
            return obj[key];
    }
}
Matt Bridges
Although this is a true, in practice all major browsers loop over the properties of an object in the order in which they were defined. This is not in the spec, of course, but it's worth a mention.
Paolo Bergantino
+7  A: 

There are no such things as associative arrays in Javascript. You can use object literals, which look like associative arrays, but they have unordered properties. Regular Javascript arrays are based on integer indexes, and can't be associative.

For example, with this object:

var params = {
    foo: 1,
    bar: 0,
    other: 2
};

You can access properties from the object, for example:

params["foo"];

And you can also iterate over the object using the for...in statement:

for(var v in params) {
    //v is equal to the currently iterated property
}

However, there is no strict rule on the order of property iteration - two iterations of your object literal could return the properties in different orders.

Perspx
Actually, JavaScript arrays can have non-integer indices as well. It just doesn't have much in the way of methods for dealing with them.
Nosredna
You mean like a string? in that case it isn't an array, just an object with properties.
roryf
Nosredna: JS arrays do not have key indices, those JS objects are considered object literals. so:foo["bar"] = 1;foo.bar === foo["bar"]; //truefoo["bar"] === foo[0]; //falseThis is one of the many subtle quirks about JS that throw people off easily.
strife25
JavaScript Objects, including Arrays, have *only* string indices. `a[1]` is actually saying `a['1']`.
bobince
+6  A: 

After reading the Wikipedia definition of associative array, I'm going to break with traditional JavaScript lore and say, "yes, JavaScript does have associative arrays." With JavaScript arrays, you can add, reassign, remove, and lookup values by their keys (and the keys can be quoted strings), which is what Wikipedia says associative arrays should be able to do.

However, you seem to be asking something different--whether you can look up the same value by either index or key. That's not a requirement of associative arrays (see the Wikipedia article.) Associative arrays don't have to give you the ability to get a value by index.

JavaScript arrays are very closely akin to JavaScript objects.

  arr=[];
  arr[0]="zero";
  arr[1]="one";
  arr[2]="two";
  arr["fancy"]="what?";

Yes, that's an array, and yes, you can get away with non-numeric indices. (If you're curious, after all this, arr.length is 3.)

In most cases, I think you should stick to numeric indices when you use arrays. That what most programmers expect, I think.

The link is to my blog post about the subject.

Nosredna
You were right when you said "And you can have an array with non-numeric indices". It slipped my mind somehow, but I know this fact.
Ionuț G. Stan
To be pedantic, "fancy" is not an index in the array, but an attribute of the array instance object.
BaroqueBobcat
Yeah, good point. That's why I say that arrays are closely related to objects in JavaScript.
Nosredna
@BaroqueBobcat: to be really pedantic :) indices in the array are just properties ("attributes") of the array instance object; an array just treats specially those properties which are the string form of an integer with regards to the length property.
Miles
+1  A: 

Native JS objects only accept strings as property names, which is true even for numeric array indices; arrays differ from vanilla objects only insofar as most JS implementations will store numerically indexed properties differently (ie in an actual array as long as they are dense) and setting them will trigger additional operations (eg adjustment of the length property).

If you're looking for a map which accepts arbitrary keys, you'll have to use a non-native implementation. The script is intended for fast iteration and not random-access by numeric indices, so it might nor be what you're looking for.

A barebones implementation of a map which would do what you're asking for could look like this:

function Map() {
    this.length = 0;
    this.store = {};
}

Map.prototype.get = function(key) {
    return this.store.hasOwnProperty(key) ?
        this.store[key] : undefined;
};

Map.prototype.put = function(key, value, index) {
    if(arguments.length < 3) {
        if(this.store.hasOwnProperty(key)) {
            this.store[key].value = value;
            return this;
        }

        index = this.length;
    }
    else if(typeof index !== 'number' || index < 0 || index >= 0xffffffff ||
        Math.floor(index) !== index)
        throw new Error('illegal index argument');

    if(index >= this.length)
        this.length = index + 1;

    this[index] = this.store[key] =
        { index : index, key : key, value : value };

    return this;
};

The index argument of put() is optional.

You can access the values in a map map either by key or index via

map.get('key').value
map[2].value
Christoph
+1, but I would lose "numerically indexed properties will be stored differently": that's not necessarily the case, and isn't required by the spec.
Miles
@Miles: [x] done
Christoph
+1  A: 
var stuff = [];
stuff[0] = "foo";
stuff.bar = stuff[0]; // stuff.bar can be stuff["bar"] if you prefer
var key = "bar";
alert(stuff[0] + ", " + stuff[key]); // shows "foo, foo"
NickFitz
but then stuff.length would not be 1 anymore since you added .bar right? thus looping through by index wouldnt really work anymore..
puffpio
No - adding a named property doesn't increase the length; only adding an element by numeric index increases length. Adding "stuff[1000] = 'blah'" would cause the length to increase to 1001, even though only two numerically-indexed elements and one named property have actually been added. Fun isn't it ;-)
NickFitz