mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 07:01:18 +08:00
Add new hash table methods.
This commit is contained in:
parent
f04626bc5c
commit
ecd8d22fc6
@ -1,14 +1,29 @@
|
||||
import LinkedList from '../linked-list/LinkedList';
|
||||
|
||||
// Hash table size directly affects on the number of collisions.
|
||||
// The bigger the hash table size the less collisions you'll get.
|
||||
// For demonstrating purposes hash table size is small to show how collisions
|
||||
// are being handled.
|
||||
const defaultHashTableSize = 32;
|
||||
|
||||
export default class HashTable {
|
||||
/**
|
||||
* @param {number} hashTableSize
|
||||
*/
|
||||
constructor(hashTableSize = defaultHashTableSize) {
|
||||
// Create hash table of certain size and fill each bucket with empty linked list.
|
||||
this.buckets = Array(hashTableSize).fill(null).map(() => new LinkedList());
|
||||
|
||||
// Just to keep track of all actual keys in a fast way.
|
||||
this.keys = {};
|
||||
}
|
||||
|
||||
// Converts key string to hash number.
|
||||
/**
|
||||
* Converts key string to hash number.
|
||||
*
|
||||
* @param {string} key
|
||||
* @return {number}
|
||||
*/
|
||||
hash(key) {
|
||||
const hash = Array.from(key).reduce(
|
||||
(hashAccumulator, keySymbol) => (hashAccumulator + keySymbol.charCodeAt(0)),
|
||||
@ -19,8 +34,14 @@ export default class HashTable {
|
||||
return hash % this.buckets.length;
|
||||
}
|
||||
|
||||
insert(key, value) {
|
||||
const bucketLinkedList = this.buckets[this.hash(key)];
|
||||
/**
|
||||
* @param {string} key
|
||||
* @param {*} value
|
||||
*/
|
||||
set(key, value) {
|
||||
const keyHash = this.hash(key);
|
||||
this.keys[key] = keyHash;
|
||||
const bucketLinkedList = this.buckets[keyHash];
|
||||
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
|
||||
|
||||
if (!node) {
|
||||
@ -32,8 +53,14 @@ export default class HashTable {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @return {*}
|
||||
*/
|
||||
delete(key) {
|
||||
const bucketLinkedList = this.buckets[this.hash(key)];
|
||||
const keyHash = this.hash(key);
|
||||
delete this.keys[key];
|
||||
const bucketLinkedList = this.buckets[keyHash];
|
||||
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
|
||||
|
||||
if (node) {
|
||||
@ -43,10 +70,29 @@ export default class HashTable {
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @return {*}
|
||||
*/
|
||||
get(key) {
|
||||
const bucketLinkedList = this.buckets[this.hash(key)];
|
||||
const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key });
|
||||
|
||||
return node ? node.value.value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {string} key
|
||||
* @return {boolean}
|
||||
*/
|
||||
has(key) {
|
||||
return Object.hasOwnProperty.call(this.keys, key);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return {string[]}
|
||||
*/
|
||||
getKeys() {
|
||||
return Object.keys(this.keys);
|
||||
}
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ describe('HashTable', () => {
|
||||
expect(hashTable.hash('abc')).toBe(6);
|
||||
});
|
||||
|
||||
it('should insert, read and delete data with collisions', () => {
|
||||
it('should set, read and delete data with collisions', () => {
|
||||
const hashTable = new HashTable(3);
|
||||
|
||||
expect(hashTable.hash('a')).toBe(1);
|
||||
@ -25,11 +25,15 @@ describe('HashTable', () => {
|
||||
expect(hashTable.hash('c')).toBe(0);
|
||||
expect(hashTable.hash('d')).toBe(1);
|
||||
|
||||
hashTable.insert('a', 'sky-old');
|
||||
hashTable.insert('a', 'sky');
|
||||
hashTable.insert('b', 'sea');
|
||||
hashTable.insert('c', 'earth');
|
||||
hashTable.insert('d', 'ocean');
|
||||
hashTable.set('a', 'sky-old');
|
||||
hashTable.set('a', 'sky');
|
||||
hashTable.set('b', 'sea');
|
||||
hashTable.set('c', 'earth');
|
||||
hashTable.set('d', 'ocean');
|
||||
|
||||
expect(hashTable.has('x')).toBeFalsy();
|
||||
expect(hashTable.has('b')).toBeTruthy();
|
||||
expect(hashTable.has('c')).toBeTruthy();
|
||||
|
||||
const stringifier = value => `${value.key}:${value.value}`;
|
||||
|
||||
@ -47,18 +51,38 @@ describe('HashTable', () => {
|
||||
expect(hashTable.get('a')).toBeNull();
|
||||
expect(hashTable.get('d')).toBe('ocean');
|
||||
|
||||
hashTable.insert('d', 'ocean-new');
|
||||
hashTable.set('d', 'ocean-new');
|
||||
expect(hashTable.get('d')).toBe('ocean-new');
|
||||
});
|
||||
|
||||
it('should be possible to add objects to hash table', () => {
|
||||
const hashTable = new HashTable();
|
||||
|
||||
hashTable.insert('objectKey', { prop1: 'a', prop2: 'b' });
|
||||
hashTable.set('objectKey', { prop1: 'a', prop2: 'b' });
|
||||
|
||||
const object = hashTable.get('objectKey');
|
||||
expect(object).toBeDefined();
|
||||
expect(object.prop1).toBe('a');
|
||||
expect(object.prop2).toBe('b');
|
||||
});
|
||||
|
||||
it('should track actual keys', () => {
|
||||
const hashTable = new HashTable(3);
|
||||
|
||||
hashTable.set('a', 'sky-old');
|
||||
hashTable.set('a', 'sky');
|
||||
hashTable.set('b', 'sea');
|
||||
hashTable.set('c', 'earth');
|
||||
hashTable.set('d', 'ocean');
|
||||
|
||||
expect(hashTable.getKeys()).toEqual(['a', 'b', 'c', 'd']);
|
||||
expect(hashTable.has('a')).toBeTruthy();
|
||||
expect(hashTable.has('x')).toBeFalsy();
|
||||
|
||||
hashTable.delete('a');
|
||||
|
||||
expect(hashTable.has('a')).toBeFalsy();
|
||||
expect(hashTable.has('b')).toBeTruthy();
|
||||
expect(hashTable.has('x')).toBeFalsy();
|
||||
});
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user