如何在JavaScript中执行关联数组/散列

我想要使​​用JavaScript计算/存储一些统计信息,C#中的等效代码在下面(我需要的function是 – 键值对,string/整型键值对,通过键操作值等),任何想法如何在JavaScript中实现相同的function? 看起来像没有内置的字典或散列表?

Dictionary<string, int> statistics; statistics["Foo"] = 10; statistics["Goo"] = statistics["Goo"] + 1; statistics.Add("Zoo", 1); 

使用JavaScript对象作为关联数组 。

关联数组:简单的说,关联数组使用string而不是整数作为索引。

创build一个对象

 var dictionary = {}; 

Javascript允许你使用下面的语法为对象添加属性:

 Object.yourProperty = value; 

另一种语法是:

 Object["yourProperty"] = value; 

如果您还可以使用以下语法创build键值对象映射

 var point = { x:3, y:2 }; point["x"] // => 3; point.y // => 2; 

您可以使用for..in循环构造如下遍历关联数组

 for(key in dict){ var value = dict[key]; /* use key/value for intended purpose */ } 
 var associativeArray = {}; associativeArray["one"] = "First"; associativeArray["two"] = "Second"; associativeArray["three"] = "Third"; 

如果你是来自面向对象的语言,你应该检查这篇文章 。

除非你有一个特定的理由不要,只是使用一个普通的对象。 Javascript中的对象属性可以使用散列表样式语法来引用:

 var hashtable = {}; hashtable.foo = "bar"; hashtable['bar'] = "foo"; 

现在可以将foobar元素引用为:

 hashtable['foo']; hashtable['bar']; // or hashtable.foo; hashtable.bar; 

当然这意味着你的钥匙必须是string。 如果他们不是string,他们内部转换为string,所以它可能仍然工作,YMMV。

所有现代浏览器都支持一个JavaScript Map对象。 使用Map比Object更好的原因有两个:

  • 一个对象有一个原型,所以在地图上有默认的键。
  • 对象的键是string,它们可以是Map的任何值。
  • 您可以轻松获得地图的大小,而您必须跟踪对象的大小。

例:

 var myMap = new Map(); var keyObj = {}, keyFunc = function () {}, keyString = "a string"; myMap.set(keyString, "value associated with 'a string'"); myMap.set(keyObj, "value associated with keyObj"); myMap.set(keyFunc, "value associated with keyFunc"); myMap.size; // 3 myMap.get(keyString); // "value associated with 'a string'" myMap.get(keyObj); // "value associated with keyObj" myMap.get(keyFunc); // "value associated with keyFunc" 

如果你想要从其他对象中没有引用的密钥被垃圾收集,可以考虑使用WeakMap而不是Map。

由于JS中的每个对象的行为都像 – 一般实现为 – 哈希表,我只是去… …

 var hashSweetHashTable = {}; 

如果你需要你的键是任何对象而不是string,那么你可以使用我的jshashtable 。

所以在C#中的代码如下所示:

 Dictionary<string,int> dictionary = new Dictionary<string,int>(); dictionary.add("sample1", 1); dictionary.add("sample2", 2); 

要么

 var dictionary = new Dictionary<string, int> { {"sample1", 1}, {"sample2", 2} }; 

在JavaScript中

 var dictionary = { "sample1": 1, "sample2": 2 } 

C#字典对象包含像JavaScript中的dictionary.ContainsKey()这样的有用的方法,我们可以使用hasOwnProperty

 if (dictionary.hasOwnProperty("sample1")) console.log("sample1 key found and its value is"+ dictionary["sample1"]); 
 function HashTable() { this.length = 0; this.items = new Array(); for (var i = 0; i < arguments.length; i += 2) { if (typeof (arguments[i + 1]) != 'undefined') { this.items[arguments[i]] = arguments[i + 1]; this.length++; } } this.removeItem = function (in_key) { var tmp_previous; if (typeof (this.items[in_key]) != 'undefined') { this.length--; var tmp_previous = this.items[in_key]; delete this.items[in_key]; } return tmp_previous; } this.getItem = function (in_key) { return this.items[in_key]; } this.setItem = function (in_key, in_value) { var tmp_previous; if (typeof (in_value) != 'undefined') { if (typeof (this.items[in_key]) == 'undefined') { this.length++; } else { tmp_previous = this.items[in_key]; } this.items[in_key] = in_value; } return tmp_previous; } this.hasItem = function (in_key) { return typeof (this.items[in_key]) != 'undefined'; } this.clear = function () { for (var i in this.items) { delete this.items[i]; } this.length = 0; } } 

https://gist.github.com/alexhawkins/f6329420f40e5cafa0a4

 var HashTable = function() { this._storage = []; this._count = 0; this._limit = 8; } HashTable.prototype.insert = function(key, value) { //create an index for our storage location by passing it through our hashing function var index = this.hashFunc(key, this._limit); //retrieve the bucket at this particular index in our storage, if one exists //[[ [k,v], [k,v], [k,v] ] , [ [k,v], [k,v] ] [ [k,v] ] ] var bucket = this._storage[index] //does a bucket exist or do we get undefined when trying to retrieve said index? if (!bucket) { //create the bucket var bucket = []; //insert the bucket into our hashTable this._storage[index] = bucket; } var override = false; //now iterate through our bucket to see if there are any conflicting //key value pairs within our bucket. If there are any, override them. for (var i = 0; i < bucket.length; i++) { var tuple = bucket[i]; if (tuple[0] === key) { //overide value stored at this key tuple[1] = value; override = true; } } if (!override) { //create a new tuple in our bucket //note that this could either be the new empty bucket we created above //or a bucket with other tupules with keys that are different than //the key of the tuple we are inserting. These tupules are in the same //bucket because their keys all equate to the same numeric index when //passing through our hash function. bucket.push([key, value]); this._count++ //now that we've added our new key/val pair to our storage //let's check to see if we need to resize our storage if (this._count > this._limit * 0.75) { this.resize(this._limit * 2); } } return this; }; HashTable.prototype.remove = function(key) { var index = this.hashFunc(key, this._limit); var bucket = this._storage[index]; if (!bucket) { return null; } //iterate over the bucket for (var i = 0; i < bucket.length; i++) { var tuple = bucket[i]; //check to see if key is inside bucket if (tuple[0] === key) { //if it is, get rid of this tuple bucket.splice(i, 1); this._count--; if (this._count < this._limit * 0.25) { this._resize(this._limit / 2); } return tuple[1]; } } }; HashTable.prototype.retrieve = function(key) { var index = this.hashFunc(key, this._limit); var bucket = this._storage[index]; if (!bucket) { return null; } for (var i = 0; i < bucket.length; i++) { var tuple = bucket[i]; if (tuple[0] === key) { return tuple[1]; } } return null; }; HashTable.prototype.hashFunc = function(str, max) { var hash = 0; for (var i = 0; i < str.length; i++) { var letter = str[i]; hash = (hash << 5) + letter.charCodeAt(0); hash = (hash & hash) % max; } return hash; }; HashTable.prototype.resize = function(newLimit) { var oldStorage = this._storage; this._limit = newLimit; this._count = 0; this._storage = []; oldStorage.forEach(function(bucket) { if (!bucket) { return; } for (var i = 0; i < bucket.length; i++) { var tuple = bucket[i]; this.insert(tuple[0], tuple[1]); } }.bind(this)); }; HashTable.prototype.retrieveAll = function() { console.log(this._storage); //console.log(this._limit); }; /******************************TESTS*******************************/ var hashT = new HashTable(); hashT.insert('Alex Hawkins', '510-599-1930'); //hashT.retrieve(); //[ , , , [ [ 'Alex Hawkins', '510-599-1930' ] ] ] hashT.insert('Boo Radley', '520-589-1970'); //hashT.retrieve(); //[ , [ [ 'Boo Radley', '520-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ] ] ] hashT.insert('Vance Carter', '120-589-1970').insert('Rick Mires', '520-589-1970').insert('Tom Bradey', '520-589-1970').insert('Biff Tanin', '520-589-1970'); //hashT.retrieveAll(); /* [ , [ [ 'Boo Radley', '520-589-1970' ], [ 'Tom Bradey', '520-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ], [ 'Rick Mires', '520-589-1970' ] ], , , [ [ 'Biff Tanin', '520-589-1970' ] ] ] */ //overide example (Phone Number Change) // hashT.insert('Rick Mires', '650-589-1970').insert('Tom Bradey', '818-589-1970').insert('Biff Tanin', '987-589-1970'); //hashT.retrieveAll(); /* [ , [ [ 'Boo Radley', '520-589-1970' ], [ 'Tom Bradey', '818-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ], [ 'Rick Mires', '650-589-1970' ] ], , , [ [ 'Biff Tanin', '987-589-1970' ] ] ] */ hashT.remove('Rick Mires'); hashT.remove('Tom Bradey'); //hashT.retrieveAll(); /* [ , [ [ 'Boo Radley', '520-589-1970' ] ], , [ [ 'Alex Hawkins', '510-599-1930' ] ], , , [ [ 'Biff Tanin', '987-589-1970' ] ] ] */ hashT.insert('Dick Mires', '650-589-1970').insert('Lam James', '818-589-1970').insert('Ricky Ticky Tavi', '987-589-1970'); hashT.retrieveAll(); /* NOTICE HOW HASH TABLE HAS NOW DOUBLED IN SIZE UPON REACHING 75% CAPACITY ie 6/8. It is now size 16. [, , [ [ 'Vance Carter', '120-589-1970' ] ], [ [ 'Alex Hawkins', '510-599-1930' ], [ 'Dick Mires', '650-589-1970' ], [ 'Lam James', '818-589-1970' ] ], , , , , , [ [ 'Boo Radley', '520-589-1970' ], [ 'Ricky Ticky Tavi', '987-589-1970' ] ], , , , , [ [ 'Biff Tanin', '987-589-1970' ] ] ] */ console.log(hashT.retrieve('Lam James')); //818-589-1970 console.log(hashT.retrieve('Dick Mires')); //650-589-1970 console.log(hashT.retrieve('Ricky Ticky Tavi')); //987-589-1970 console.log(hashT.retrieve('Alex Hawkins')); //510-599-1930 console.log(hashT.retrieve('Lebron James')); //null 

我创build了这个来实现一些问题,比如对象键映射,枚举能力(用forEach()方法)和清除。

 function Hashtable() { this._map = new Map(); this._indexes = new Map(); this._keys = []; this._values = []; this.put = function(key, value) { var newKey = !this.containsKey(key); this._map.set(key, value); if (newKey) { this._indexes.set(key, this.length); this._keys.push(key); this._values.push(value); } }; this.remove = function(key) { if (!this.containsKey(key)) return; this._map.delete(key); var index = this._indexes.get(key); this._indexes.delete(key); this._keys.splice(index, 1); this._values.splice(index, 1); }; this.indexOfKey = function(key) { return this._indexes.get(key); }; this.indexOfValue = function(value) { return this._values.indexOf(value) != -1; }; this.get = function(key) { return this._map.get(key); }; this.entryAt = function(index) { var item = {}; Object.defineProperty(item, "key", { value: this.keys[index], writable: false }); Object.defineProperty(item, "value", { value: this.values[index], writable: false }); return item; }; this.clear = function() { var length = this.length; for (var i = 0; i < length; i++) { var key = this.keys[i]; this._map.delete(key); this._indexes.delete(key); } this._keys.splice(0, length); }; this.containsKey = function(key) { return this._map.has(key); }; this.containsValue = function(value) { return this._values.indexOf(value) != -1; }; this.forEach = function(iterator) { for (var i = 0; i < this.length; i++) iterator(this.keys[i], this.values[i], i); }; Object.defineProperty(this, "length", { get: function() { return this._keys.length; } }); Object.defineProperty(this, "keys", { get: function() { return this._keys; } }); Object.defineProperty(this, "values", { get: function() { return this._values; } }); Object.defineProperty(this, "entries", { get: function() { var entries = new Array(this.length); for (var i = 0; i < entries.length; i++) entries[i] = this.entryAt(i); return entries; } }); } 

Hashtable类的文档

方法:

  • get(key)
    返回与指定键关联的值。
    参数:
    key :从中检索值的键。

  • put(key, value)
    将指定的值关联到指定的键。
    参数:
    key :关联值的关键。
    value :与键关联的值。

  • remove(key)
    使用其值删除指定的键。
    参数:
    key :要删除的键。

  • clear()
    清除所有哈希表,删除键和值。

  • indexOfKey(key)
    根据添加顺序返回指定键的索引。
    参数:
    key :获取索引的关键。

  • indexOfValue(value)
    根据添加顺序返回指定值的索引。
    参数:
    value :其中的价值获得索引。
    笔记:
    这个信息是通过一个数组的indexOf()方法来检索的,所以它只是将对象与toString()方法进行比较。

  • entryAt(index)
    返回具有两个属性的对象:键和值,表示指定索引处的条目。
    参数:
    index :要获取的条目的索引。

  • containsKey(key)
    返回散列表是否包含指定的键。
    参数:
    key :检查的关键。

  • containsValue(value)
    返回哈希表是否包含指定的值。
    参数:
    value :要检查的值。

  • forEach(iterator)
    迭代指定iterator中的所有条目。
    参数:
    value :一个带有3个参数的方法: keyvalueindex ,其中index表示条目的索引。

    属性:

  • length只读
    获取散列表中条目的计数。

  • keys只读
    获取散列表中所有键的数组。

  • values只读
    获取哈希表中的所有值的数组。

  • entries只读
    获取散列表中所有条目的数组。 它们以与entryAt()方法相同的forms表示。