Как создать ассоциативный массив / хэш / отображение ключ-значение в JavaScript?
Я пытаюсь сохранить некоторые статистические данные с использованием JavaScript так, как я это делал бы в C#. Вот пример на C#:
Dictionary<string, int> statistics;
statistics["Foo"] = 10;
statistics["Goo"] = statistics["Goo"] + 1;
statistics.Add("Zoo", 1);
Существует ли эквивалент Hashtable
или что-то вроде Dictionary<TKey, TValue>
в JavaScript? Как я могу хранить значения таким образом?
5 ответ(ов)
В общем случае, если у вас нет конкретной причины использовать что-то иное, просто используйте обычный объект. Свойства объектов в JavaScript можно обращаться с помощью синтаксиса, подобного хеш-таблицам:
var hashtable = {};
hashtable.foo = "bar";
hashtable['bar'] = "foo";
Теперь элементы foo
и bar
можно будет обращаться следующим образом:
hashtable['foo'];
hashtable['bar'];
// Или
hashtable.foo;
hashtable.bar;
Стоит отметить, что ключи в объектах должны быть строками. Если они не строковые, они будут автоматически преобразованы во внутренние строки, так что это все равно может сработать. Однако поведение может варьироваться, учитывать это стоит.
В JavaScript каждый объект действительно ведет себя как хеш-таблица и обычно реализуется таким образом. Это означает, что вы можете использовать объекты для хранения пар "ключ-значение" в удобном формате. Если вам нужно создать простую хеш-таблицу, лучше всего использовать литерал объекта:
var hashSweetHashTable = {};
Таким образом, вы можете добавлять, изменять и извлекать значения по ключам, используя этот объект. Это простой и эффективный способ работы с данными в JavaScript.
В 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 найден, и его значение равно " + dictionary["sample1"]);
Таким образом, оба языка предлагают свои способы работы со словарями, но есть определенные различия в синтаксисе и доступных методах.
Примечание:
Несколько лет назад я реализовал следующую хеш-таблицу, в которой недоставало некоторых функций, присущих классу Map
. Однако сейчас это уже не так — теперь можно итерироваться по записям Map
, получать массив его ключей или значений (или и тех, и других), удалять конкретные элементы по их ключу и очищать всю карту.
Таким образом, моя реализация хеш-таблицы полезна только для совместимости, и было бы разумнее написать полноценный полифф для этих целей.
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
: Значение, для которого необходимо получить индекс.Примечания:
Значения сравниваются по идентичности.
entryAt(index)
Возвращает объект с свойствами
key
иvalue
, представляющий запись по указанному индексу.Параметры:
index
: Индекс записи, которую нужно получить.
containsKey(key)
Возвращает, содержит ли хеш-таблица указанный ключ.
Параметры:
key
: Ключ для поиска.
containsValue(value)
Возвращает, содержит ли хеш-таблица указанное значение.
Параметры:
value
: Значение для поиска.
forEach(iterator)
Итерирует по всем записям в хеш-таблице, вызывая указанный
iterator
.Параметры:
iterator
: Метод с тремя параметрами:key
,value
иindex
, гдеindex
представляет индекс записи в порядке добавления.
Свойства:
length
(Только для чтения)Получает количество записей в хеш-таблице.
keys
(Только для чтения)Получает массив всех ключей в хеш-таблице.
values
(Только для чтения)Получает массив всех значений в хеш-таблице.
entries
(Только для чтения)Получает массив всех записей в хеш-таблице. Они представлены так же, как и метод
entryAt()
.
Ваш код представляет собой реализацию хэш-таблицы на JavaScript. В целом, он выглядит вполне функционально, но есть несколько моментов, которые стоит обсудить.
Конструктор: Вы правильно начинаете с метода
HashTable
, который инициализирует хэш-таблицу. Однако используйтеObject.create
для создания объектов в JavaScript вместо использования массива для хранения элементов.Использование
arguments
: Доступ кarguments
позволяет передавать пары ключ-значение, что достаточно удобно. Но стоит отметить, что вы должны быть осторожны с четностью количества передаваемых аргументов, чтобы избежать обращения к несуществующим индексам.Методы:
- removeItem: Этот метод корректно удаляет элемент по ключу, но стоит вернуть
undefined
, если ключ не найден, а не переменнуюtmp_previous
, которая может содержать предыдущие значения. - getItem: Метод работает корректно, возвращая значение по ключу.
- setItem: Этот метод выглядит хорошо, но имейте в виду, что если новый элемент добавляется,
length
увеличивается. - hasItem: Вернет
true
, если элемент существует, иfalse
в противном случае. Этого достаточно для проверки наличия ключа. - clear: Этот метод эффективно очищает хэш-таблицу, но стоит учитывать, что перезапись массива - это более безопасный способ.
- removeItem: Этот метод корректно удаляет элемент по ключу, но стоит вернуть
Вот ваша структура с небольшими изменениями для улучшения читаемости и безопасности:
function HashTable() {
this.length = 0;
this.items = {};
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 (this.hasItem(in_key)) {
tmp_previous = this.items[in_key];
delete this.items[in_key];
this.length--;
}
return tmp_previous !== undefined ? tmp_previous : null;
}
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 (!this.hasItem(in_key)) {
this.length++;
} else {
tmp_previous = this.items[in_key];
}
this.items[in_key] = in_value;
}
return tmp_previous;
}
this.hasItem = function(in_key) {
return this.items.hasOwnProperty(in_key);
}
this.clear = function() {
this.items = {};
this.length = 0;
}
}
Некоторые изменения, которые я внес:
- Перешёл на объект вместо массива для хранения элементов.
- Добавил метод
hasOwnProperty
для проверки наличия ключа. - Устранены потенциальные неопределённые ссылки для возвратов значений.
Надеюсь, это поможет улучшить вашу реализацию хэш-таблицы!
Существует ли реализация словаря в JavaScript?
Где найти документацию по форматированию даты в JavaScript?
Ошибка: "'dict' объект не имеет метода 'iteritems'"
Как определить нажатие клавиши Esc?
Как остановить Babel от трансформации 'this' в 'undefined' и добавления "use strict"