我需要使用JavaScript存储一些类似于C#中的统计数据:
Dictionary<string, int> statistics;
statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
在JavaScript中是否有类似Hashtable
或Dictionary<TKey, TValue>
的东西?
我该如何以这种方式存储值?
我需要使用JavaScript存储一些类似于C#中的统计数据:
Dictionary<string, int> statistics;
statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
在JavaScript中是否有类似Hashtable
或Dictionary<TKey, TValue>
的东西?
我该如何以这种方式存储值?
关联数组:简单来说,关联数组使用字符串而不是整数作为索引。
创建一个对象:
var dictionary = {};
Object.yourProperty = value;
Object["yourProperty"] = value;
var point = { x:3, y:2 };
point["x"] // returns 3
point.y // returns 2
for(var key in Object.keys(dict)){
var value = dict[key];
/* use key/value for intended purpose */
}
new Array()
初始化“关联数组”的方法已经不被推荐。文章最终提到了它的缺点,并建议使用 new Object()
或 {}
作为首选替代方案,但这几乎是在文章末尾,我担心大多数读者无法看到这里。 - Daniel Lubarovvar obj1 = {}; var obj2 = {}; var table= {}; table[obj1] = "A"; table[obj2] = "B"; alert(table[obj1]); //显示 B
,因为它无法区分键obj1和obj2;它们都被转换为字符串并变成类似于“Object”的内容。这是一个完全的失败,在JavaScript中让类型安全的序列化保持引用和循环引用不变是困难或者性能不佳的。而在Flash/AS3中很容易实现。 - Triynkofor...in
循环会遍历它的键,因此 Object.keys
在那里似乎放错了位置。Object.keys
返回字典的键数组,而对于数组,for...in
循环遍历的是 它的 “键”,对于数组来说是它的索引,而不是它的值。 - JHHvar associativeArray = {};
associativeArray["one"] = "First";
associativeArray["two"] = "Second";
associativeArray["three"] = "Third";
如果你来自一门面向对象的编程语言,你应该查看这篇文章。
所有现代浏览器都支持JavaScript的Map对象。以下是使用Map比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。
Map
几乎没有用处。 - Siyuan Ren|基本支持|
Chrome: v38;
Edge: v12;
Firefox: v13;
IE: v11;
Opera: v25;
Safari: v7.1;
但仍不适用于移动设备:|基本支持|
Chrome: v38;
Android: 无;
Firefox: v13;
IE Mobile: 无;
Opera Mobile: 无;
Safari Mobile: v8;
- kayleeFrye_onDeck除非你有特殊原因不使用,否则请使用普通对象。在 JavaScript 中,可以使用哈希表风格的语法引用对象属性:
Unless you have a specific reason not to, just use a normal object. Object properties in JavaScript can be referenced using hashtable-style syntax:var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";
现在可以将foo
和bar
元素都称为:
hashtable['foo'];
hashtable['bar'];
// Or
hashtable.foo;
hashtable.bar;
当然,这意味着你的键必须是字符串。如果它们不是字符串,则会在内部转换为字符串,因此可能仍然可用。但结果可能有所不同。
var hash = {}; hash[1] = "foo"; alert(hash["1"]);
弹出窗口显示 "foo"。 - Tim Downvar dict = {}; dict.key1 = "val1"; dict["key2"] = "val2";
,dict的key1元素可以通过dict["key1"]
和dict.key1
等价地引用。 - Jim由于JavaScript中的每个对象都像哈希表一样运作,并且通常是以哈希表实现的,因此我就沿用了这种方式...
var hashSweetHashTable = {};
hashSweetHashTable.foo
被设置,那么应该进入 if 代码块。 - Koray Tugay在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"]);
hasOwnProperty
的答案。 - brichins几年前,我实现了以下哈希表,它有一些 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;
}
});
}
get(key)
返回指定键所关联的值。
参数:
key
:要检索值的键。
put(key, value)
将指定的值关联到指定的键上。
参数:
key
:要关联值的键。
value
:要关联到键上的值。
remove(key)
移除指定的键及其关联的值。
参数:
key
:要移除的键。
clear()
清空整个哈希表,即移除所有条目。
indexOfKey(key)
按条目添加的顺序返回指定键的索引。
参数:
key
:要获取索引的键。
indexOfValue(value)
按条目添加的顺序返回指定值的索引。
参数:
value
:要获取索引的值。
备注:
此处使用的是“引用相等(identity)”而非“值相等(equality)”进行比较。
entryAt(index)
返回一个对象,该对象具有“key”和“value”属性,表示指定索引处的条目。
参数:
index
:要获取条目的索引。
containsKey(key)
返回哈希表是否包含指定的键。
参数:
key
:要查找的键。
containsValue(value)
返回散列表是否包含指定值。
参数:
value
:要查找的值。
forEach(iterator)
遍历散列表中的所有条目,调用指定的iterator
方法。
参数:
iterator
:一个具有三个参数的方法,key
、value
和index
,其中index
表示按添加顺序排列的条目的索引。
length
(只读)获取散列表中条目的数量。
keys
(只读)获取散列表中所有键的数组。
values
(只读)获取散列表中所有值的数组。
entries
(只读)获取散列表中所有条目的数组。它们与方法entryAt()
表示的方式相同。
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;
}
}
{}
而不是数组:[]
或new Array()
,否则js引擎会出现问题——它将看到1个变量的2种类型,这意味着无法优化,或者它将运行数组并意识到必须更改为对象(可能需要重新分配)。 - Graeme Wickstedhttps://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