如何在JavaScript中做关联数组/哈希

我需要使用JavaScript存储一些统计数据,就像我在c#中做的那样:

Dictionary<string, int> statistics;


statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
JavaScript中有HashtableDictionary<TKey, TValue>之类的东西吗?< br > 我如何以这种方式存储值呢?< / p >
806972 次浏览

由于JavaScript中的每个对象都像哈希表一样,并且通常是作为哈希表实现的,所以我就这样…

var hashSweetHashTable = {};

除非你有特别的理由不这样做,否则就用一个普通的对象。JavaScript中的对象属性可以使用哈希表样式的语法引用:

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

foobar元素现在都可以被引用为:

hashtable['foo'];
hashtable['bar'];


// Or
hashtable.foo;
hashtable.bar;

当然,这意味着你的键必须是字符串。如果它们不是字符串,它们会在内部转换为字符串,所以它仍然可以工作。你的里程可能会有所不同。

使用# EYZ0。

关联数组:简单地说,关联数组使用字符串而不是整数作为索引。

使用

var dictionary = {};

JavaScript允许您使用以下语法向对象添加属性:

Object.yourProperty = value;

相同的另一种语法是:

Object["yourProperty"] = value;

如果可以,也可以使用以下语法创建键到值的对象映射:

var point = { x:3, y:2 };


point["x"] // returns 3
point.y // returns 2

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

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

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

如果你需要你的键是任何对象,而不仅仅是字符串,那么你可以使用我的jshashtable

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) {


// Override 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' ] ] ]
*/


// Override 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 THE HASH TABLE HAS NOW DOUBLED IN SIZE UPON REACHING 75% CAPACITY, i.e. 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

所有现代浏览器都支持JavaScript 地图对象。有几个原因使得使用Map比使用Object更好:

  • Object有一个原型,所以映射中有默认键。
  • 对象的键是字符串,它们可以是Map的任何值。
  • 当你必须跟踪对象的大小时,你可以很容易地获得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。

在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#字典对象包含有用的方法,如dictionary.ContainsKey()

在JavaScript中,我们可以像这样使用hasOwnProperty:

if (dictionary.hasOwnProperty("sample1"))
console.log("sample1 key found and its value is"+ dictionary["sample1"]);
< h3 id = " note-yijz " >注意:< / h3 > 几年前,我实现了以下哈希表,其中有一些功能是Map类所缺少的。然而,情况不再是这样了——现在,可以遍历Map的条目,获得它的键或值的数组或两者都有(这些操作是复制到新分配的数组中实现的,尽管如此——这是一种浪费内存,它的时间复杂度总是像O(n)一样慢),删除给定键的特定项,并清除整个映射。
因此,我的哈希表实现仅用于兼容性目的,在这种情况下,基于此编写适当的polyfill将是一个更明智的方法
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

. Hashtable < h2 id = "方法" >方法:< / h2 >
  • < h3 id = " getkey-towf " > # EYZ0

    返回与指定键相关联的值。

    < p > # EYZ0

    . key:从中检索值的键

  • < h3 id = " putkey-value-lqpr " > # EYZ0

    将指定的值关联到指定的键。

    < p > # EYZ0
    key:与值相关联的键。
    value:与键相关联的值

  • < h3 id = " removekey-w5i9 " > # EYZ0

    删除指定的键以及与之关联的值。

    < p > # EYZ0
    key:删除键

  • < h3 id = " clear-xj0i " > # EYZ0

    清除整个哈希表,通过删除其所有条目。


  • < h3 id = " indexofkeykey-udz4 " > # EYZ0

    返回指定键的索引,根据已添加的订单项。

    < p > # EYZ0
    key:用于获取索引的键

  • < h3 id = " indexofvaluevalue-bn1h " > # EYZ0

    返回指定值的索引,根据已添加的订单项。

    < p > # EYZ0
    value:获取索引的值 < p > # EYZ0


  • < h3 id = " entryatindex-bo0f " > # EYZ0

    返回一个具有keyvalue属性的对象,表示指定索引处的条目。

    < p > # EYZ0
    index:要获取的条目的索引

  • < h3 id = " containskeykey-2i9p " > # EYZ0

    返回哈希表是否包含指定的键。

    < >强参数: key:要查找的键


  • < h3 id = " containsvaluevalue-qufd " > # EYZ0

    返回哈希表是否包含指定的值。

    < p > # EYZ0
    value:要查找的值

  • < h3 id = " foreachiterator-bvvr " > # EYZ0

    遍历哈希表中的所有条目,调用指定的iterator

    < p > # EYZ0
    iterator:一个有三个参数的方法,keyvalueindex,其中index表示条目的索引,根据它被添加的顺序
< h2 id = "属性" >属性:< / h2 >
  • length (只读)

    获取哈希表中条目的计数。

  • keys (只读)

    获取哈希表中所有键的数组。

  • values (只读)

    获取哈希表中所有值的数组。

  • entries (只读)

    获取哈希表中所有项的数组。它们的表示方式与方法entryAt()相同。

你可以像下面这样创建一个:

var dictionary = { Name:"Some Programmer", Age:24, Job:"Writing Programs"  };


// Iterate over using keys
for (var key in dictionary) {
console.log("Key: " + key + " , " + "Value: "+ dictionary[key]);
}


// Access a key using object notation:
console.log("Her name is: " + dictionary.Name)