Как сделать ассоциативный массив/хеширование в JavaScript
Мне нужно сохранить некоторые статистические данные с помощью JavaScript так, как если бы я сделал это на С#:
Dictionary<string, int> statistics;
statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
Есть ли в JavaScript Hashtable
или что-то вроде Dictionary<TKey, TValue>
?
Как я могу хранить значения таким образом?
Ответы
Ответ 1
Использовать объекты JavaScript как ассоциативные массивы.
Ассоциативный массив. В простых словах ассоциативные массивы используют строки вместо целых чисел как индекс.
Создайте объект с помощью
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 */
}
Ответ 2
var associativeArray = {};
associativeArray["one"] = "First";
associativeArray["two"] = "Second";
associativeArray["three"] = "Third";
Если вы исходите из объектно-ориентированного языка, вы должны проверить эту статью.
Ответ 3
Если у вас нет конкретной причины, просто используйте обычный объект. Свойства объекта в Javascript можно ссылаться с помощью синтаксиса стиля hashtable:
var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";
В обоих элементах foo
и bar
теперь можно ссылаться как:
hashtable['foo'];
hashtable['bar'];
// or
hashtable.foo;
hashtable.bar;
Конечно, это означает, что ваши ключи должны быть строками. Если они не являются строками, они преобразуются внутренне в строки, поэтому они могут работать, YMMV.
Ответ 4
Все современные браузеры поддерживают javascript Map объект. Есть несколько причин, которые делают использование карты лучше, чем Object:
- Объект имеет прототип, поэтому на карте есть ключи по умолчанию.
- Ключами объекта являются строки, где они могут быть любым значением для Карты.
- Вы можете легко получить размер карты, пока вам нужно отслеживать размер объекта.
Пример:
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 вместо карты.
Ответ 5
Поскольку каждый объект в JS ведет себя как - и обычно реализуется как хэш-таблица, я просто иду с этим...
var hashSweetHashTable = {};
Ответ 6
Если вам нужно, чтобы ваши ключи были любыми объектами, а не просто строками, вы можете использовать jshashtable.
Ответ 7
поэтому в С# код выглядит следующим образом:
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
}
Словарь С# содержит полезные методы, такие как dictionary.ContainsKey()
в JavaScript мы могли бы использовать hasOwnProperty
как
if (dictionary.hasOwnProperty("sample1"))
console.log("sample1 key found and its value is"+ dictionary["sample1"]);
Ответ 8
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;
}
}
Ответ 9
Я создал это для решения некоторых проблем, таких как сопоставление ключей объектов, способность перечисления (с помощью метода 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
: метод с тремя параметрами: key
, value
и index
, где index
представляет индекс записи.
Свойства
-
length
& ЕПРС; (только чтение)
Возвращает количество записей в хэш-таблице.
-
keys
& ЕПРС; (только чтение)
Получает массив всех ключей в хэш-таблице.
-
values
& ЕПРС; (только чтение)
Получает массив всех значений в хэш-таблице.
-
entries
& ЕПРС; (только чтение)
Получает массив всех записей в хэш-таблице. Они представлены в том же виде метода entryAt()
.
Ответ 10
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 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
Ответ 11
Вы можете создать его так, как показано ниже:
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)