Today, we're going to talk about a very common data structure that is built into almost every programming language. In JavaScript, they are referred to as objects or Maps.
Hash tables are used to store key-value pairs, but do you really know how they work? Before diving into the mechanics, let's start with why we use them.
Imagine working with city coordinates. Storing them in an array might look like this:
const coordinates = ["123.123", "456.456", "789.789"];
Retrieving coordinates from an array requires knowing the index, which isn't user-friendly otherwise we can use the find utility function but then we have a performance issue because every value has to be visited for comparison.
Using a hash table makes retrieval more intuitive:
const cities = { Paris: "123.123", London: "456.456", Amsterdam: "789.789" };
cities["Paris"]; // "123.123"
For each inserted value, a hash is generated. This hash must respect the following rules:
Note: We won't dive into hash functions here, as it is a complex field involving advanced mathematics, especially for maximum security. But you can learn more here
There are two main options for handling collisions:
For simplicity, we will use separate chaining.
The HashTable class represents our custom data structure, which is essentially an array.
class HashTable {
constructor(size = 30) {
this.map = new Array(size);
}
}
This hash table is based on an array of a predefined size. The array can grow past this size, but setting a size allows us to reserve just the amount of memory we need.
The set function adds a key-value pair to the hash table.
function set(key, value) {
const index = hash(key, this.map.length + 1);
if (!this.map[index]) this.map[index] = [];
this.map[index].push([key, value]);
return index;
}
Explanation:
const hash = new HashTable(5);
hash.length; // 5
hash.set("Paris", "123.123"); // 4
// [null, null, null, [["Paris", "123.123"]], null]
In case of a collision, the next inserted value would also be added at the same index.
hash.set("London", "456.456"); // 4
// [null, null, null, [["Paris", "123.123"], ["London", "456.456"]], null]
The get function retrieves a value based on a key.
function get(key) {
const index = hash(key, this.map.length + 1);
const results = this.map[index];
if (!results) return undefined;
const foundItem = results.find((item) => item[0] === key);
return foundItem ? foundItem[1] : undefined;
}
Explanation:
So if we tried to retrieve our previously added value:
hash.get("Paris"); // "123.123";
The keys function returns all the keys in the hash table.
function keys() {
const keys = [];
for (let i = 0; i < this.map.length; i++) {
if (this.map[i]) {
for (let j = 0; j < this.map[i].length; j++) {
keys.push(this.map[i][j][0]);
}
}
}
return keys;
}
Explanation:
The values function returns all the values in the hash table.
function values() {
const values = [];
for (let i = 0; i < this.map.length; i++) {
if (this.map[i]) {
for (let j = 0; j < this.map[i].length; j++) {
values.push(this.map[i][j][1]);
}
}
}
return values;
}
Explanation:
Hash tables are incredibly versatile and can be used in various real-world scenarios:
Hash tables offer excellent performance for insertion, deletion, and retrieval operations:
Hash tables are a fundamental data structure that provides efficient key-value storage and retrieval. While built-in data structures like JavaScript's Map are typically sufficient, understanding how to implement a hash table can be valuable for educational purposes and specific edge cases.
Thanks for reading! If you have any questions or recommendations, please leave a comment. I might learn something new.
Happy coding!
Honestly i am no one, i've just been coding for 3 years now and i like to document every solutions to every problem i encounter. Extracting as much code snippets and tutorials i can so that if i ever need it again i just have to pop back here and get a quick refresher.
Feel free to me through this learning journey by providing any feedback and if you wanna support me: