diff --git a/skeletons/data-structures/hash-table/HashTable.js b/skeletons/data-structures/hash-table/HashTable.js index 8c9ce7f..a2fbd37 100644 --- a/skeletons/data-structures/hash-table/HashTable.js +++ b/skeletons/data-structures/hash-table/HashTable.js @@ -1,51 +1,42 @@ -import LinkedList from '../linked-list/LinkedList'; +import LinkedList from '../../../src/data-structures/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; +const DEFAULT_SIZE = 32; export default class HashTable { /** * @param {number} hashTableSize */ - constructor(hashTableSize = defaultHashTableSize) {} + constructor(size = DEFAULT_SIZE) {} /** * Converts key string to hash number. - *asadsd + * * @param {string} key * @return {number} */ hash(key) {} - /** - * @param {string} key - * @param {*} value - */ - set(key, value) {} - /** * @param {string} key * @return {*} */ - delete(key) {} + get(key) {} /** * @param {string} key - * @return {*} + * @return {boolean} */ - get(key) {} + has(key) {} /** * @param {string} key - * @return {boolean} + * @param {*} value */ - has(key) {} + set(key, value) {} /** - * @return {string[]} + * @param {string} key + * @return {*} */ - getKeys() {} + delete(key) {} } diff --git a/skeletons/data-structures/hash-table/README.md b/skeletons/data-structures/hash-table/README.md index 59b5194..2ca408d 100644 --- a/skeletons/data-structures/hash-table/README.md +++ b/skeletons/data-structures/hash-table/README.md @@ -1,26 +1,111 @@ # Hash Table -In computing, a hash table (hash map) is a data -structure which implements an associative array -abstract data type, a structure that can map keys -to values. A hash table uses a hash function to -compute an index into an array of buckets or slots, -from which the desired value can be found +## Description -Ideally, the hash function will assign each key to a -unique bucket, but most hash table designs employ an -imperfect hash function, which might cause hash -collisions where the hash function generates the same -index for more than one key. Such collisions must be -accommodated in some way. +Watch both videos about hash tables: -![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg) + +[Data Structures: Hash Tables](https://www.youtube.com/watch?v=0M_kIqhwbFo) (6 Minutes) -Hash collision resolved by separate chaining. + +[Hash Tables and Hash Functions by Kevin Drumm](https://www.youtube.com/watch?v=KyUTuwz_b7Q) (14 Minutes) -![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg) +The first video provides a great introduction to the what a hash table is, and what it's used for. The second video provides the explanation of the actual implementation (and the speaker has an awesome British accent). -## References +## Implementation -- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table) -- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) +- `constructor(size)` + - Write a function that instantiates the buckets. +- `hash(key)` + - Write a function that generates the hash of the key. +- `get(key)` + - Write a function that retrieves an item from the hash table. +- `has(key)` + - Write a function that returns whether or not the item exists in the hash. +- `set(key, value)` + - Write a function that sets an item in the hash table. +- `delete(key)` + - Write a function that deletes an item in the hash table. + +## Walkthrough + +### `constructor()` + +- Usage: + ``` + const hashTable = new HashTable(); + ``` +- Create an array with the size specified. +- Each index in the array is usually called a bucket. +- Fill each bucket in the array with a linked list. + +### `hash(key)` + +- Generate the index of the bucket. +- Usage: + ``` + const hash = this.hash('a') // hash => 1 + ``` +- There are numerous ways to generate a hash, and for this exercise, we will simply map `a` to `1`, `b` to `2`, `c` to `3` and on. + - Examples: + ``` + hash('a') // 1 + hash('b') // 2 + hash('ab') // 3 = 1 + 2 + hash('abc') // 6 = 1 + 2 + 3 + ``` +- Once the hash value is generated, `mod` it by the length of the buckets to map it to the buckets. + +### `get(key)` + +- Retrieves the value for the stored key. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + ``` +- Utilize the `this.hash(key)` funtion in order to access the find the linked list. +- Utilize the found linked list's `find` function to locate the linked list node with the value. + - The find function also takes a custom callback. + - Use the callback function to find the node with the correct key + +### `has(key)` + +- Returns whether the key exists in the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.has('a'); // false + ``` +- Utilize the `get()` function written previously to return whether the hash table contains the key. + +### `set(key, value)` + +- Stores the key/value pair into the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + hashTable.set('a', 2); + hashTable.get('a'); // 2 + ``` +- Locate the correct bucket by utilizing the `hash()` function. +- Check if the linked list in the bucket contains an entry with the key. +- If the key exists, delete the key. + - When you call `set('a', 1)` then `set('a', 2)`, the expected value is `2`. +- Add a new entry for the key/value pair to the linked list. + +### `delete(key)` + +- Delete the key/value pair from the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.delete('a'); + hashTable.has('a'); // false + ``` diff --git a/skeletons/data-structures/hash-table/__test__/HashTable.test.js b/skeletons/data-structures/hash-table/__test__/HashTable.test.js index cc32215..6e7a1fb 100644 --- a/skeletons/data-structures/hash-table/__test__/HashTable.test.js +++ b/skeletons/data-structures/hash-table/__test__/HashTable.test.js @@ -17,6 +17,20 @@ describe('HashTable', () => { expect(hashTable.hash('abc')).toBe(6); }); + it('should be possible to add objects to hash table', () => { + const hashTable = new HashTable(); + + 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 set, read and delete data with collisions', () => { const hashTable = new HashTable(3); @@ -43,47 +57,16 @@ describe('HashTable', () => { expect(hashTable.get('a')).toBe('sky'); expect(hashTable.get('d')).toBe('ocean'); - expect(hashTable.get('x')).not.toBeDefined(); + expect(hashTable.get('x')).toBeNull(); hashTable.delete('a'); expect(hashTable.delete('not-existing')).toBeNull(); - expect(hashTable.get('a')).not.toBeDefined(); + expect(hashTable.get('a')).toBeNull(); expect(hashTable.get('d')).toBe('ocean'); 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.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(); - }); }); diff --git a/solutions/data-structures/hash-table/HashTable.js b/solutions/data-structures/hash-table/HashTable.js index 51575f9..e3aa96f 100644 --- a/solutions/data-structures/hash-table/HashTable.js +++ b/solutions/data-structures/hash-table/HashTable.js @@ -1,21 +1,13 @@ -import LinkedList from '../linked-list/LinkedList'; +import LinkedList from '../../../solutions/data-structures/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; +const DEFAULT_SIZE = 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 = {}; + constructor(size = DEFAULT_SIZE) { + this.buckets = Array(size).fill().map(() => new LinkedList()); } /** @@ -25,74 +17,72 @@ export default class HashTable { * @return {number} */ hash(key) { - const hash = Array.from(key).reduce( - (hashAccumulator, keySymbol) => (hashAccumulator + keySymbol.charCodeAt(0)), - 0, - ); + const hashNum = Array.from(key).reduce((hash, char) => hash += char.charCodeAt(0), 0); + return hashNum % this.buckets.length; + } + + /** + * @param {string} key + * @return {*} + */ + get(key) { + const hash = this.hash(key); + + const foundItem = this.buckets[hash].find({ + callback: nodeValue => nodeValue.key === key, + }); - // Reduce hash number so it would fit hash table size. - return hash % this.buckets.length; + return foundItem && foundItem.value.value; } /** * @param {string} key - * @param {*} value + * @return {boolean} */ - 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) { - // Insert new node. - bucketLinkedList.append({ key, value }); - } else { - // Update value of existing node. - node.value.value = value; - } + has(key) { + return !!this.get(key); } /** * @param {string} key - * @return {*} + * @param {*} value */ - delete(key) { - const keyHash = this.hash(key); - delete this.keys[key]; - const bucketLinkedList = this.buckets[keyHash]; - const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key }); + set(key, value) { + const hash = this.hash(key); + const bucket = this.buckets[hash]; + + const node = this.buckets[hash].find({ + callback: nodeValue => nodeValue.key === key, + }) if (node) { - return bucketLinkedList.delete(node.value); + bucket.delete(node.value); } - return null; + bucket.append({ + key, + value + }); } /** * @param {string} key * @return {*} */ - get(key) { - const bucketLinkedList = this.buckets[this.hash(key)]; - const node = bucketLinkedList.find({ callback: nodeValue => nodeValue.key === key }); + delete(key) { + if (this.has(key)) { + const hash = this.hash(key); + const bucket = this.buckets[hash]; - return node ? node.value.value : undefined; - } + const node = bucket.find({ + callback: value => value.key === key, + }) - /** - * @param {string} key - * @return {boolean} - */ - has(key) { - return Object.hasOwnProperty.call(this.keys, key); - } + bucket.delete(node.value); - /** - * @return {string[]} - */ - getKeys() { - return Object.keys(this.keys); + return node; + } + + return null; } } diff --git a/solutions/data-structures/hash-table/README.md b/solutions/data-structures/hash-table/README.md index 59b5194..2ca408d 100644 --- a/solutions/data-structures/hash-table/README.md +++ b/solutions/data-structures/hash-table/README.md @@ -1,26 +1,111 @@ # Hash Table -In computing, a hash table (hash map) is a data -structure which implements an associative array -abstract data type, a structure that can map keys -to values. A hash table uses a hash function to -compute an index into an array of buckets or slots, -from which the desired value can be found +## Description -Ideally, the hash function will assign each key to a -unique bucket, but most hash table designs employ an -imperfect hash function, which might cause hash -collisions where the hash function generates the same -index for more than one key. Such collisions must be -accommodated in some way. +Watch both videos about hash tables: -![Hash Table](https://upload.wikimedia.org/wikipedia/commons/7/7d/Hash_table_3_1_1_0_1_0_0_SP.svg) + +[Data Structures: Hash Tables](https://www.youtube.com/watch?v=0M_kIqhwbFo) (6 Minutes) -Hash collision resolved by separate chaining. + +[Hash Tables and Hash Functions by Kevin Drumm](https://www.youtube.com/watch?v=KyUTuwz_b7Q) (14 Minutes) -![Hash Collision](https://upload.wikimedia.org/wikipedia/commons/d/d0/Hash_table_5_0_1_1_1_1_1_LL.svg) +The first video provides a great introduction to the what a hash table is, and what it's used for. The second video provides the explanation of the actual implementation (and the speaker has an awesome British accent). -## References +## Implementation -- [Wikipedia](https://en.wikipedia.org/wiki/Hash_table) -- [YouTube](https://www.youtube.com/watch?v=shs0KM3wKv8&index=4&list=PLLXdhg_r2hKA7DPDsunoDZ-Z769jWn4R8) +- `constructor(size)` + - Write a function that instantiates the buckets. +- `hash(key)` + - Write a function that generates the hash of the key. +- `get(key)` + - Write a function that retrieves an item from the hash table. +- `has(key)` + - Write a function that returns whether or not the item exists in the hash. +- `set(key, value)` + - Write a function that sets an item in the hash table. +- `delete(key)` + - Write a function that deletes an item in the hash table. + +## Walkthrough + +### `constructor()` + +- Usage: + ``` + const hashTable = new HashTable(); + ``` +- Create an array with the size specified. +- Each index in the array is usually called a bucket. +- Fill each bucket in the array with a linked list. + +### `hash(key)` + +- Generate the index of the bucket. +- Usage: + ``` + const hash = this.hash('a') // hash => 1 + ``` +- There are numerous ways to generate a hash, and for this exercise, we will simply map `a` to `1`, `b` to `2`, `c` to `3` and on. + - Examples: + ``` + hash('a') // 1 + hash('b') // 2 + hash('ab') // 3 = 1 + 2 + hash('abc') // 6 = 1 + 2 + 3 + ``` +- Once the hash value is generated, `mod` it by the length of the buckets to map it to the buckets. + +### `get(key)` + +- Retrieves the value for the stored key. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + ``` +- Utilize the `this.hash(key)` funtion in order to access the find the linked list. +- Utilize the found linked list's `find` function to locate the linked list node with the value. + - The find function also takes a custom callback. + - Use the callback function to find the node with the correct key + +### `has(key)` + +- Returns whether the key exists in the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.has('a'); // false + ``` +- Utilize the `get()` function written previously to return whether the hash table contains the key. + +### `set(key, value)` + +- Stores the key/value pair into the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + hashTable.set('a', 2); + hashTable.get('a'); // 2 + ``` +- Locate the correct bucket by utilizing the `hash()` function. +- Check if the linked list in the bucket contains an entry with the key. +- If the key exists, delete the key. + - When you call `set('a', 1)` then `set('a', 2)`, the expected value is `2`. +- Add a new entry for the key/value pair to the linked list. + +### `delete(key)` + +- Delete the key/value pair from the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.delete('a'); + hashTable.has('a'); // false + ``` diff --git a/solutions/data-structures/hash-table/__test__/HashTable.test.js b/solutions/data-structures/hash-table/__test__/HashTable.test.js index cc32215..6e7a1fb 100644 --- a/solutions/data-structures/hash-table/__test__/HashTable.test.js +++ b/solutions/data-structures/hash-table/__test__/HashTable.test.js @@ -17,6 +17,20 @@ describe('HashTable', () => { expect(hashTable.hash('abc')).toBe(6); }); + it('should be possible to add objects to hash table', () => { + const hashTable = new HashTable(); + + 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 set, read and delete data with collisions', () => { const hashTable = new HashTable(3); @@ -43,47 +57,16 @@ describe('HashTable', () => { expect(hashTable.get('a')).toBe('sky'); expect(hashTable.get('d')).toBe('ocean'); - expect(hashTable.get('x')).not.toBeDefined(); + expect(hashTable.get('x')).toBeNull(); hashTable.delete('a'); expect(hashTable.delete('not-existing')).toBeNull(); - expect(hashTable.get('a')).not.toBeDefined(); + expect(hashTable.get('a')).toBeNull(); expect(hashTable.get('d')).toBe('ocean'); 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.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(); - }); }); diff --git a/solutions/data-structures/linked-list/LinkedList.js b/solutions/data-structures/linked-list/LinkedList.js index 855feee..47a1109 100644 --- a/solutions/data-structures/linked-list/LinkedList.js +++ b/solutions/data-structures/linked-list/LinkedList.js @@ -66,7 +66,7 @@ export default class LinkedList { if (currentNode !== null) { // If next node must be deleted then make next node to be a next next one. - while (currentNode.next) { + while (currentNode && currentNode.next) { if (currentNode.next.value === value) { deletedNode = currentNode.next; currentNode.next = currentNode.next.next; diff --git a/src/data-structures/hash-table/HashTable.js b/src/data-structures/hash-table/HashTable.js new file mode 100644 index 0000000..a2fbd37 --- /dev/null +++ b/src/data-structures/hash-table/HashTable.js @@ -0,0 +1,42 @@ +import LinkedList from '../../../src/data-structures/linked-list/LinkedList'; + +const DEFAULT_SIZE = 32; + +export default class HashTable { + /** + * @param {number} hashTableSize + */ + constructor(size = DEFAULT_SIZE) {} + + /** + * Converts key string to hash number. + * + * @param {string} key + * @return {number} + */ + hash(key) {} + + /** + * @param {string} key + * @return {*} + */ + get(key) {} + + /** + * @param {string} key + * @return {boolean} + */ + has(key) {} + + /** + * @param {string} key + * @param {*} value + */ + set(key, value) {} + + /** + * @param {string} key + * @return {*} + */ + delete(key) {} +} diff --git a/src/data-structures/hash-table/README.md b/src/data-structures/hash-table/README.md new file mode 100644 index 0000000..2ca408d --- /dev/null +++ b/src/data-structures/hash-table/README.md @@ -0,0 +1,111 @@ +# Hash Table + +## Description + +Watch both videos about hash tables: + + +[Data Structures: Hash Tables](https://www.youtube.com/watch?v=0M_kIqhwbFo) (6 Minutes) + + +[Hash Tables and Hash Functions by Kevin Drumm](https://www.youtube.com/watch?v=KyUTuwz_b7Q) (14 Minutes) + +The first video provides a great introduction to the what a hash table is, and what it's used for. The second video provides the explanation of the actual implementation (and the speaker has an awesome British accent). + +## Implementation + +- `constructor(size)` + - Write a function that instantiates the buckets. +- `hash(key)` + - Write a function that generates the hash of the key. +- `get(key)` + - Write a function that retrieves an item from the hash table. +- `has(key)` + - Write a function that returns whether or not the item exists in the hash. +- `set(key, value)` + - Write a function that sets an item in the hash table. +- `delete(key)` + - Write a function that deletes an item in the hash table. + +## Walkthrough + +### `constructor()` + +- Usage: + ``` + const hashTable = new HashTable(); + ``` +- Create an array with the size specified. +- Each index in the array is usually called a bucket. +- Fill each bucket in the array with a linked list. + +### `hash(key)` + +- Generate the index of the bucket. +- Usage: + ``` + const hash = this.hash('a') // hash => 1 + ``` +- There are numerous ways to generate a hash, and for this exercise, we will simply map `a` to `1`, `b` to `2`, `c` to `3` and on. + - Examples: + ``` + hash('a') // 1 + hash('b') // 2 + hash('ab') // 3 = 1 + 2 + hash('abc') // 6 = 1 + 2 + 3 + ``` +- Once the hash value is generated, `mod` it by the length of the buckets to map it to the buckets. + +### `get(key)` + +- Retrieves the value for the stored key. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + ``` +- Utilize the `this.hash(key)` funtion in order to access the find the linked list. +- Utilize the found linked list's `find` function to locate the linked list node with the value. + - The find function also takes a custom callback. + - Use the callback function to find the node with the correct key + +### `has(key)` + +- Returns whether the key exists in the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.has('a'); // false + ``` +- Utilize the `get()` function written previously to return whether the hash table contains the key. + +### `set(key, value)` + +- Stores the key/value pair into the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.get('a'); // 1 + hashTable.set('a', 2); + hashTable.get('a'); // 2 + ``` +- Locate the correct bucket by utilizing the `hash()` function. +- Check if the linked list in the bucket contains an entry with the key. +- If the key exists, delete the key. + - When you call `set('a', 1)` then `set('a', 2)`, the expected value is `2`. +- Add a new entry for the key/value pair to the linked list. + +### `delete(key)` + +- Delete the key/value pair from the hash table. +- Usage: + ``` + const hashTable = new HashTable(); + hashTable.set('a', 1); + hashTable.delete('a'); + hashTable.has('a'); // false + ``` diff --git a/src/data-structures/hash-table/__test__/HashTable.test.js b/src/data-structures/hash-table/__test__/HashTable.test.js new file mode 100644 index 0000000..6e7a1fb --- /dev/null +++ b/src/data-structures/hash-table/__test__/HashTable.test.js @@ -0,0 +1,72 @@ +import HashTable from '../HashTable'; + +describe('HashTable', () => { + it('should create hash table of certain size', () => { + const defaultHashTable = new HashTable(); + expect(defaultHashTable.buckets.length).toBe(32); + + const biggerHashTable = new HashTable(64); + expect(biggerHashTable.buckets.length).toBe(64); + }); + + it('should generate proper hash for specified keys', () => { + const hashTable = new HashTable(); + + expect(hashTable.hash('a')).toBe(1); + expect(hashTable.hash('b')).toBe(2); + expect(hashTable.hash('abc')).toBe(6); + }); + + it('should be possible to add objects to hash table', () => { + const hashTable = new HashTable(); + + 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 set, read and delete data with collisions', () => { + const hashTable = new HashTable(3); + + expect(hashTable.hash('a')).toBe(1); + expect(hashTable.hash('b')).toBe(2); + expect(hashTable.hash('c')).toBe(0); + expect(hashTable.hash('d')).toBe(1); + + 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}`; + + expect(hashTable.buckets[0].toString(stringifier)).toBe('c:earth'); + expect(hashTable.buckets[1].toString(stringifier)).toBe('a:sky,d:ocean'); + expect(hashTable.buckets[2].toString(stringifier)).toBe('b:sea'); + + expect(hashTable.get('a')).toBe('sky'); + expect(hashTable.get('d')).toBe('ocean'); + expect(hashTable.get('x')).toBeNull(); + + hashTable.delete('a'); + + expect(hashTable.delete('not-existing')).toBeNull(); + + expect(hashTable.get('a')).toBeNull(); + expect(hashTable.get('d')).toBe('ocean'); + + hashTable.set('d', 'ocean-new'); + expect(hashTable.get('d')).toBe('ocean-new'); + }); +});