javascript关联数组的键需要是字符串,还是可以是任何对象?

时间:2009-02-04 19:09:55

标签: javascript arrays

javascript关联数组的键需要是字符串,还是可以是任何对象?

5 个答案:

答案 0 :(得分:16)

JavaScript中没有本地关联数组,只有对象。对象具有属性。属性的名称总是字符串:甚至在“数组魔术”发生之前的数组will be converted to strings的数字索引。

如果您正在寻找具有任意键的关联数组look here

答案 1 :(得分:2)

我已经实现了javascript HashMap,可以从http://github.com/lambder/HashMapJS/tree/master

获取代码

键和值可以是任意javascript对象。 对用作键或值的对象没有要求。

这种机制是微不足道的。对于每个键,都会生成唯一的id(每个HashMap实例)。 该id被注入到极不可能碰撞字段名称的密钥对象;)

然后,该ID用于键入底层烘焙标准javascript关联对象。

以下是代码:

/*
 =====================================================================
 @license MIT
 @author Lambder
 @copyright 2009 Lambder.
 @end
 =====================================================================
 */
var HashMap = function() {
  this.initialize();
}

HashMap.prototype = {
  hashkey_prefix: "<#HashMapHashkeyPerfix>",
  hashcode_field: "<#HashMapHashkeyPerfix>",

  initialize: function() {
    this.backing_hash = {};
    this.code = 0;
  },
  /*
   maps value to key returning previous assocciation
   */
  put: function(key, value) {
    var prev;
    if (key && value) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        prev = this.backing_hash[hashCode];
      } else {
        this.code += 1;
        hashCode = this.hashkey_prefix + this.code;
        key[this.hashcode_field] = hashCode;
      }
      this.backing_hash[hashCode] = value;
    }
    return prev;
  },
  /*
   returns value associated with given key
   */
  get: function(key) {
    var value;
    if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        value = this.backing_hash[hashCode];
      }
    }
    return value;
  },
  /*
   deletes association by given key.
   Returns true if the assocciation existed, false otherwise
   */
  del: function(key) {
    var success = false;
    if (key) {
      var hashCode = key[this.hashcode_field];
      if (hashCode) {
        var prev = this.backing_hash[hashCode];
        this.backing_hash[hashCode] = undefined;
        if(prev !== undefined)
          success = true;
      }
    }
    return success;
  }
}

//// Usage

// creation

var my_map = new HashMap();

// insertion

var a_key = {};
var a_value = {struct: "structA"};
var b_key = {};
var b_value = {struct: "structB"};
var c_key = {};
var c_value = {struct: "structC"};

my_map.put(a_key, a_value);
my_map.put(b_key, b_value);
var prev_b = my_map.put(b_key, c_value);

// retrieval

if(my_map.get(a_key) !== a_value){
  throw("fail1")
}
if(my_map.get(b_key) !== c_value){
  throw("fail2")
}
if(prev_b !== b_value){
  throw("fail3")
}

// deletion

var a_existed = my_map.del(a_key);
var c_existed = my_map.del(c_key);
var a2_existed = my_map.del(a_key);

if(a_existed !== true){
  throw("fail4")
}
if(c_existed !== false){
  throw("fail5")
}
if(a2_existed !== false){
  throw("fail6")
}

BonAppétit, Lambder

答案 2 :(得分:1)

您在谈论Javascript对象(JSON)吗?

规范说密钥应该是字符串http://json.org/

但Javascript解释器同时允许{"key": "val"}{key: "val"}

答案 3 :(得分:0)

基于Lambder的想法,我实现了一个小型DataStructures库。

我已经测试了一下,一切似乎都有效。

它还会自动为每个HashTable / HashSet分配一个唯一的id,用于唯一标识对象的关键属性。

var DataStructure = {};

DataStructure.init = function(){
DataStructure.initHashables();
delete DataStructure.initHashables;
}

DataStructure.initHashables = function(){
var objectHashableIndexer = new DataStructure.Indexer();

DataStructure.Hashable = function(){
    var self = this;

    // Constant
    //
    //
    const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in Hashable when trying to pop. Associated Key Object and Hashable logged to console.";
    const HASH_MAP_KEY_PROPERTY_BASE = "DATA_STRUCTURE_HASH_MAP_KEY_PROPERTY_";

    // Attributes
    //
    //
    var tableNumber = objectHashableIndexer.getIndex();
    var tableKeyProperty = HASH_MAP_KEY_PROPERTY_BASE + tableNumber.toString();
    self.tableKeyProperty = tableKeyProperty;
    var indexer = new DataStructure.Indexer();
    var data = {};
    self.data = data;

    // Methods
    //
    //
    self.getObjectKey = function(){
        return indexer.getIndex().toString();
    }

    self.putBackObjectKey = function(index){
        indexer.putBackIndex(parseInt(index));
    }

    var getObjectKey = self.getObjectKey;
    var putBackObjectKey = self.putBackObjectKey;

    self.exists = function(key){
        if (!(tableKeyProperty in key))
            return false;

        var realKey = key[tableKeyProperty];

        if (!(realKey in data))
            return false;

        return true;
    }

    self.pop = function(key){
        if (!self.exists(key)){
            console.log(key);
            console.log(self);
            throw ERROR_KEY_DOES_NOT_EXIST;
        }
        else{
            var realKey = key[tableKeyProperty];
            delete key[tableKeyProperty];
            delete data[realKey];
            putBackObjectKey(realKey);
        }
    }

    self.destroy = function(){
        objectHashableIndexer.putBackIndex(tableNumber);
        delete self;
    }

}

/*
    Class DataStructure.ObjectHashMap
        Purpose: Provides a way to hash arbitrary objects to values.

        Prototype Arguments:

        Attributes:

        Methods:

        Notes:
            Should call inherited method destroy() when done with table to preserve indexes
*/
DataStructure.ObjectHashMap = function(){
    DataStructure.Hashable.call(this);
    var self = this;

    // Constant
    //
    //
    const ERROR_KEY_EXISTS = "Key already exists in ObjectHashMap when trying to push. Associated Key Object and ObjectHashMap logged to console.";
    const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in ObjectHashMap when trying to getValue. Associated Key Object and ObjectHashMap logged to console.";

    // Attributes
    //
    //

    var tableKeyProperty;
    var data;

    // Initialization
    //
    //
    self.init = function(){
        self.privatize();
        delete self.privatize;
    }

    self.privatize = function(){
        tableKeyProperty = self.tableKeyProperty;
        delete self.tableKeyProperty;

        getObjectKey = self.getObjectKey;
        delete self.getObjectKey;

        putBackObjectKey = self.putBackObjectKey;
        delete self.putBackObjectKey;

        data = self.data;
        delete self.data;
    }

    // Methods
    //
    //
    var getObjectKey;
    var putBackObjectKey;

    self.push = function(key, value){
        if (self.exists(key)){
            console.log(key);
            console.log(self);
            throw ERROR_KEY_EXISTS;
        }
        else{
            var realKey = getObjectKey();
            key[tableKeyProperty] = realKey;
            data[realKey] = value;
        }
    }

    self.getValue = function(key){
        if(!self.exists(key)){
            console.log(key);
            console.log(self);
            throw ERROR_KEY_DOES_NOT_EXIST;
        }
        else{
            var realKey = key[tableKeyProperty];
            return data[realKey];
        }
    }

    self.init();
    delete self.init;
}

/*
    Class DataStructure.ObjectHashSet
        Purpose: Provides a way to store arbitrary objects and check that they exist.

        Prototype Arguments:

        Attributes:

        Methods:

        Notes:
            Should call inherited method destroy() when done with table to preserve indexes
*/
DataStructure.ObjectHashSet = function(){
    DataStructure.Hashable.call(this);
    var self = this;

    // Constant
    //
    //
    const ERROR_KEY_EXISTS = "Key already exists in ObjectHashSet when trying to push. Associated Key Object and ObjectHashSet logged to console.";
    const ERROR_KEY_DOES_NOT_EXIST = "Key doesn't exists in ObjectHashSet when trying to getValue. Associated Key Object and ObjectHashSet logged to console.";

    // Attributes
    //
    //

    var tableKeyProperty;
    var data;

    // Initialization
    //
    //
    self.init = function(){
        self.privatize();
        delete self.privatize;
    }

    self.privatize = function(){
        tableKeyProperty = self.tableKeyProperty;
        delete self.tableKeyProperty;

        getObjectKey = self.getObjectKey;
        delete self.getObjectKey;

        putBackObjectKey = self.putBackObjectKey;
        delete self.putBackObjectKey;

        data = self.data;
        delete self.data;
    }

    // Methods
    //
    //
    var getObjectKey;
    var putBackObjectKey;

    self.push = function(key){
        if (self.exists(key)){
            console.log(key);
            console.log(self);
            throw ERROR_KEY_EXISTS;
        }
        else{
            var realKey = getObjectKey();
            key[tableKeyProperty] = realKey;
            data[realKey] = "";
        }
    }

    self.init();
    delete self.init;
}

}

DataStructure.Indexer = function(){
var self = this;

// Constant
//
//
const DEFAULT_SIZE = 1000;

// Attributes
//
//
var nextIndex = 0;
var availableIndicies = 0;
var freeIndicies = [];

// Initialization
//
//
self.init = function(){
    freeIndicies.length = DEFAULT_SIZE;
}

// Methods
//
//
self.getIndex = function(){
    var index = 0;

    if (availableIndicies === 0){
        index = nextIndex;
        ++nextIndex;
    }
    else{
        --availableIndicies;
        index = freeIndicies[availableIndicies];
    }

    return index;
}

self.putBackIndex = function(index){
    if (availableIndicies === freeIndicies.length)
        freeIndicies.push(index);
    else
        freeIndicies[availableIndicies] = index;

    ++availableIndicies;
}

self.init();
delete self.init;
}

DataStructure.init();
delete DataStructure.init;

答案 4 :(得分:0)

这取决于您所说的“关联数组”。 JavaScript中没有所谓的“关联数组”,没有对象,也有地图。

对象是可以使用[]表示法访问的对象,例如foo["bar"],并且键必须是字符串,如Christoph's answer所述。

还有maps,可以将任何对象用作键。但是,要访问它们,不能像对象一样使用[],必须使用getset方法。这是一个如何使用它们的示例:

let myMap = new Map();    // Create the map
myMap.set("key", "value");    // To set a value, use the set method.
                            // The first argument is the key, the second one is the value.
myMap.set(Math, "bar");    // You can really use any object as key
myMap.set(console.log, "hello");    // Including functions
myMap.set(document.body, "world");    // And even DOM elements

// To get the value associated to a key, use the get method
console.log(myMap.get(Math));    // "bar"
console.log(myMap.get(document.body));    // "world"

在此示例中,我使用内置对象作为键,以避免在定义新对象以用作键的情况下使示例混乱,但是当然可以将自己的对象用作键。

但是请注意,不要使用[]访问地图元素。进行myMap[whatever]是有效的代码,因此不会引发错误,但不能按预期工作:

// Don't do this
myMap[Math] = 3;
myMap["[object Math]"] = 4;
console.log(myMap[Math]);    //4
console.log(myMap.get(Math));    // 'undefined'

// Do this instead
myMap.set(Math, 3);
myMap.set("[object Math]", 4);
console.log(myMap.get(Math));    //3

要了解有关地图的更多信息,请参见 Map