function HashTable(a, b) {
this._storage = [];
this._limit = 8;
this._size = 0;
}
HashTable.prototype.insert = function(key, value) {
const index = hash(key, this._limit);
const oldSize = this._size;
if (!this._storage[index]) {
this._storage[index] = [[key, value]];
this._size++;
} else {
let hasKey = false;
const bucket = this._storage[index];
for (let i = 0; i < bucket.length; i++) {
const [entryKey, entryValue] = bucket[i];
if (entryKey === key) {
hasKey = true;
bucket[i][1] = value;
}
}
if (!hasKey) {
bucket.push([key, value]);
this._size++;
}
}
if (this._size !== oldSize && this._size > this._limit * 0.75) {
this._resize(this._limit * 2);
}
};
HashTable.prototype.remove = function(key) {
const index = hash(key, this._limit);
const bucket = this._storage[index];
let entryIndex = -1;
if (!bucket) return false;
for (let i = 0; i < bucket.length; i++) {
const [entryKey, entryValue] = bucket[i];
if (entryKey === key) {
entryIndex = i;
}
}
bucket.splice(entryIndex, 1);
this._size--;
if (this._size < 0.25 * this._limit) {
this._resize(Math.floor(this._limit / 2));
}
};
HashTable.prototype.retrieve = function(key) {
const index = hash(key, this._limit);
if (!this._storage[index]) return undefined;
const bucket = this._storage[index];
let result;
bucket.forEach((entry) => {
[entryKey, entryValue] = entry;
if (entryKey === key) {
result = entryValue;
}
});
return result;
};
HashTable.prototype._resize = function(newLimit) {
const oldStorage = this._storage;
newLimit = Math.max(newLimit, 8);
if (newLimit === this._limit) return;
this._limit = newLimit;
this._size = 0;
this._storage = [];
oldStorage.forEach(bucket => {
if (!bucket) return;
for (let i = 0; i < bucket.length; i++) {
const [key, value] = bucket[i];
this.insert(key, value);
}
});
};
function hash(string, max) {
let ret, i;
for(ret = 0, i = 0, len = string.length; i < len; i++) {
ret = (31 * ret + string.charCodeAt(i)) << 0;
}
return ret % max;
}