HashMap Implementation from Scratch in DSA Typescript - Time & Space Complexity
When building a HashMap from scratch, it's important to know how fast it can find, add, or remove items.
We want to understand how the time to do these actions changes as the number of stored items grows.
Analyze the time complexity of the following code snippet.
class SimpleHashMap {
private buckets: Array<[string, any][]> = [];
private size = 10;
private hash(key: string): number {
let hash = 0;
for (const char of key) {
hash += char.charCodeAt(0);
}
return hash % this.size;
}
set(key: string, value: any): void {
const index = this.hash(key);
if (!this.buckets[index]) this.buckets[index] = [];
for (const pair of this.buckets[index]) {
if (pair[0] === key) {
pair[1] = value;
return;
}
}
this.buckets[index].push([key, value]);
}
}
This code creates a simple HashMap with a fixed number of buckets and handles collisions by storing pairs in arrays.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: Loop inside
setmethod that checks for existing keys in a bucket array. - How many times: Up to the number of items stored in that bucket, which depends on collisions.
As more items are added, some buckets get longer arrays, making the inner loop longer.
| Input Size (n) | Approx. Operations in set() |
|---|---|
| 10 | About 1 to 2 checks per insertion |
| 100 | About 5 to 10 checks per insertion if collisions grow |
| 1000 | Could be up to 100 checks per insertion if many collisions |
Pattern observation: Without resizing, collisions cause the time to grow roughly proportional to the number of items in a bucket, which can grow with total items.
Time Complexity: O(n) in worst case
This means in the worst case, adding or finding an item can take time proportional to the total number of items if many keys collide.
[X] Wrong: "HashMap operations always take constant time no matter what."
[OK] Correct: If many keys end up in the same bucket, operations slow down because we must check each item there.
Understanding how collisions affect HashMap speed shows you know the real-world limits of this data structure, which is a valuable skill.
"What if we increase the number of buckets and resize the array when it gets full? How would the time complexity change?"
Practice
HashMap in data structures?Solution
Step 1: Understand HashMap functionality
A HashMap stores data as key-value pairs, allowing quick access by key.Step 2: Compare with other data structures
Unlike arrays or lists, HashMaps do not store data sequentially or sorted but focus on fast lookup.Final Answer:
To store key-value pairs for fast data retrieval -> Option BQuick Check:
HashMap = key-value fast access [OK]
- Thinking HashMap stores data in order
- Confusing HashMap with arrays
- Assuming HashMap is for calculations
Solution
Step 1: Identify TypeScript syntax for object property
Declaring a private property with type {[key: string]: any} initializes an object for key-value storage.Step 2: Check other options for correctness
map = new Map(); uses built-in Map, not a plain object; C is an array; D has wrong Map type usage.Final Answer:
private map: {[key: string]: any} = {}; -> Option AQuick Check:
TypeScript object property syntax = private map: {[key: string]: any} = {}; [OK]
- Using array instead of object for map
- Incorrect Map type declaration
- Confusing built-in Map with plain object
class SimpleHashMap {
private map: {[key: string]: string} = {};
set(key: string, value: string) {
this.map[key] = value;
}
get(key: string) {
return this.map[key] || null;
}
}
const h = new SimpleHashMap();
h.set('a', 'apple');
h.set('b', 'banana');
console.log(h.get('a'));
console.log(h.get('c'));Solution
Step 1: Trace set method calls
Keys 'a' and 'b' are set with values 'apple' and 'banana' respectively in the map object.Step 2: Trace get method calls
Getting 'a' returns 'apple'; getting 'c' returns undefined, so || null returns null.Final Answer:
"apple" followed by null -> Option AQuick Check:
Existing key returns value, missing key returns null [OK]
- Expecting undefined instead of null for missing keys
- Confusing set and get method effects
- Assuming runtime error on missing key
class BuggyHashMap {
private map: {[key: string]: number} = {};
set(key: string, value: number) {
this.map[key] = value;
}
get(key: string): number {
return this.map[key];
}
}
const h = new BuggyHashMap();
h.set('x', 10);
console.log(h.get('y'));Solution
Step 1: Analyze get method return type
get returns number but missing keys return undefined, causing type mismatch.Step 2: Fix get method to handle missing keys
Change return type to number | null and return null if key missing to avoid undefined.Final Answer:
get should return number | null and handle missing keys -> Option CQuick Check:
Handle missing keys in get method [OK]
- Ignoring undefined return for missing keys
- Changing map to array incorrectly
- Thinking set method needs key existence check
Solution
Step 1: Understand collision handling by chaining
Chaining stores multiple entries in a bucket array; new entries are added or updated if key exists.Step 2: Analyze options for correct collision handling
Compute hash, check bucket array; if key exists update value, else push new entry correctly checks bucket, updates existing key or adds new entry; others ignore collisions or do not hash.Final Answer:
Compute hash, check bucket array; if key exists update value, else push new entry -> Option DQuick Check:
Chaining = bucket array with update or add [OK]
- Overwriting bucket ignoring existing keys
- Not using hashing for bucket selection
- Storing keys and values separately without hashing
Ready to prove it?
Pass the test and this pattern moves unknown
