Hash Tables
By: Joe Lorenzo
Tags: Programming, JavaScript, Software Development, Hash Table, Data Science

There are many ways to store data in programming, one of them is known as a hash table. Javascript has its own variations of hashing, like the new Map functionality that was introduced with ECMAScript 2015 or ES6, which creates an associative array and remembers the insertion order. In a hash table, as opposed to a typical array, however, the keys are put through a hashing function, and then a numerical value is assigned to the data, and it’s placed in that location in the table, set inside what’s called a bucket. The bucket that it is inserted into can contain only one associative array at a time before there is what is called a collision. There are multiple ways of handling a collision.

One is called separate chaining, in which the array that one bucket is in is expanded to fit more arrays leading to something that resembles (or could actually be) a singly linked list, or an array of arrays depending on the implementation. This only occurs if the same indexed bucket is returned for multiple items after we pass our items through the hashing function.

Let’s suppose there is an associative array in bucket 007, if a second array is introduced because of the output of the hash function is the same as the first. It will become a multidimensional array that can house another pair, so now bucket 007 will have 2 associative arrays set within the same bucket. This method is known as open hashing. A closed hashing technique would be something like linear probing. With linear probing, if a bucket is full then the next one will be used to store the information. If the sequential one is in use it will continue running until it finds an open bucket and places it there.

Let’s talk about opened vs closed hashing. It’s called open hashing because with separate chaining there is a list that is introduced and the hash table becomes open to more than just buckets. A linked list or similar structure is placed in buckets so if a collision occurs it can just point to the next node or index. With closed hashing, the table is always intact and the only information introduced into the buckets is the associative array, and there is no need to introduce any other types of data. So what are the hash functions that determine which bucket is used? That’s completely up to the person writing the function. Hash functions can be simple or complex, but as the table expands you want to make sure that you have as few collisions as possible.

Let’s say you want to save your shopping list. You know you need eggs, but you always forget how many to buy, do you get one dozen or four? The easiest thing to do is associate eggs, and how many dozens, and put them together so that eggs points to the amount needed like so.

"eggs" => "1 dozen"

But for some odd reason, you want to do this from scratch instead of using one of the pre-existing methods for storing this. How would you accomplish this? For this we’ll use an array as the table we insert data into. We will pass the string “eggs” through a hashing function, and the output will be the index of where this key will be stored. The hash function when given key multiple times, should always point to the same place in the array. So if “eggs” is stored in an index of 1, the next time I type eggs into the hash function, I should get that same index. This is called “deterministic”. If you don’t get the same value back then something has gone wrong with the hashing function, because the output is determined(deterministic) by the input. So what does a simple hashing function look like? Again, hashing functions vary in complexity, and there are many different things you can incorporate into a hash function. Keep in mind, you want there to be some variation in the output so that different buckets are selected more frequently than not. Here is a very simple hash function.

class HashTable {

constructor(size=10) {

this.map = new Array(size);

}

_hash(key) {

let total = 0;

for (let i = 0; i < key.length; i++) {

const value = key[i].charCodeAt(0) - 96;

total += total + value;

}

return total % this.map.length;

}

}

const hashTable = new HashTable(); console.log(hashTable._hash("eggs")); // 1

console.log(hashTable._hash("eggs")); // 1

console.log(hashTable._hash("bacon")); // 6

console.log(hashTable._hash("bacon")); // 6

This function will be used to hash our keys and assign them a number. I’ve assigned it to a class called HashTable so that we can use it again and again. The underscore in front of the hash (key) denotes that it is a private function. As you can see, if “eggs” is the input, it gives the same output; the same for bacon. This is a good indication that the hash function is working well. So now that the hash function is working, it’s time to get started on setting the key-value pairs so that we can remember how many we need for our recipe. The method will be called set, and the parameters will be a key and a value. This will map the value to the bucket that is assigned to the key and it will be stored there for later retrieval. For example, when we put in eggs as a key, with the value of a dozen, it should lead us to a value of 1, which is the index of the array that the bucket is located in. So here goes the set method for the table and its results.

For the set method on the hashTable, we pass a key and a value. The function takes the key parameter and passes it through the hashing function that we created earlier. This will give an index of 1, and if there is not an array at the index of 1, then it will set one. Remember that this is one way of doing it. We’re using separate chaining, instead of linear probing, so if an array does already exist in that spot, we just create a new array and push it to create a nested array in that index. When hashTable is logged to the console after setting [“eggs”, “1 dozen”] it will look like this.