javascript array associative AND indexed?

2019-01-04 11:22发布

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?

10条回答
老娘就宠你
2楼-- · 2019-01-04 12:07

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];
    }
}
查看更多
贪生不怕死
3楼-- · 2019-01-04 12:08

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.

查看更多
放我归山
4楼-- · 2019-01-04 12:08

The tide has changed on this one. Now you can do that... and MORE! Using Harmony Proxies you could definitely solve this problem in many ways.

You'll have to verify that your targeted environments support this with maybe a little help from the harmony-reflect shim.

There's a really good example on the Mozilla Developer Network on using a Proxy to find an array item object by it's property which pretty much sums it up.

Here's my version:

  var players = new Proxy(
  [{
    name: 'monkey',
    score: 50
  }, {
    name: 'giraffe',
    score: 100
  }, {
    name: 'pelican',
    score: 150
  }], {
    get: function(obj, prop) {
      if (prop in obj) {
        // default behavior
        return obj[prop];
      }
      if (typeof prop == 'string') {

        if (prop == 'rank') {
          return obj.sort(function(a, b) {
            return a.score > b.score ? -1 : 1;
          });
        }

        if (prop == 'revrank') {
          return obj.sort(function(a, b) {
            return a.score < b.score ? -1 : 1;
          });
        }

        var winner;
        var score = 0;
        for (var i = 0; i < obj.length; i++) {
          var player = obj[i];
          if (player.name == prop) {
            return player;
          } else if (player.score > score) {
            score = player.score;
            winner = player;
          }
        }

        if (prop == 'winner') {
          return winner;
        }
        return;
      }

    }
  });

  console.log(players[0]); // { name: 'monkey', score: 50 }
  console.log(players['monkey']); // { name: 'monkey', score: 50 }
  console.log(players['zebra']); // undefined
  console.log(players.rank); // [ { name: 'pelican', score: 150 },{ name: 'giraffe', score: 100 }, { name: 'monkey', score: 50 } ]
  console.log(players.revrank); // [ { name: 'monkey', score: 50 },{ name: 'giraffe', score: 100 },{ name: 'pelican', score: 150 } ]
  console.log(players.winner); // { name: 'pelican', score: 150 }
查看更多
可以哭但决不认输i
5楼-- · 2019-01-04 12:12

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(index >>> 0 !== index || index >= 0xffffffff)
        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
查看更多
登录 后发表回答