Foundations
HashMap
Key-value lookups in average O(1) time: the single most useful trick for turning O(n²) into O(n).
How It Works
A HashMap stores key-value pairs and gives you average O(1) lookup, insertion, and deletion by keys, regardless of how many entries it holds. It achieves this by running each key through a hash function, which converts the key into a number (a bucket index). The map stores the value in that bucket, so looking a key up again means re-hashing it and jumping straight to the right bucket instead of scanning every entry.
When two different keys hash to the same bucket (a collision), the implementation stores both entries in that bucket (often as a small list) and falls back to a linear scan within just that bucket. Collisions are rare with a good hash function and enough buckets, which is why average-case performance stays O(1); worst case (many collisions), however, degrades to O(n).
In JavaScript you have two main tools:
Map: the purpose-built hash map. Keys can be any type (objects, functions, NaN), preserves insertion order, has a real.size, and is iterable directly.- Plain object (
{}): works as a hash map for string/symbol keys but comes with baggage: prototype chain properties, keys silently coerced to strings, and no easy.size.
Prefer Map over plain objects for hashmap logic: it's explicit about its purpose and avoids inherited-property bugs.
Core operations and their complexity:
get(key)/set(key, value)/has(key)/delete(key): average O(1).- Iteration: O(n).
// Map is the idiomatic hashmap in JS
const inventory = new Map();
inventory.set("apples", 5);
inventory.set("bananas", 12);
console.log(inventory.get("apples")); // 5
console.log(inventory.has("cherries")); // false
inventory.set("apples", inventory.get("apples") + 1); // update
inventory.delete("bananas");
// Classic interview pattern: two-sum in O(n) using a hashmap
function twoSum(nums, target) {
const seen = new Map(); // value -> index
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (seen.has(complement)) {
return [seen.get(complement), i];
}
seen.set(nums[i], i);
}
return [];
}
twoSum([2, 7, 11, 15], 9); // [0, 1]
Common Mistakes
- Using a plain object instead of
Mapand hitting prototype pollution:obj["toString"]returns a function, notundefined, which breaks naive "does this key exist" checks. UseMaporObject.create(null)if you must use an object. - Forgetting object keys are coerced to strings:
{ [1]: "a" }and{ "1": "a" }are the same key; if you need numeric or object keys, useMap. - Assuming O(1) is guaranteed, not average-case: under adversarial hash collisions it degrades; rarely relevant in interviews but worth knowing.
- Iterating an object with
for...in: this walks inherited enumerable properties too; useObject.keys/entriesor, better, just useMap. - Re-deriving a lookup you already built: a common interview miss is scanning the array again after already building a frequency/index map, turning an O(n) solution back into O(n²).
Pattern Summary
Whenever a problem involves "have I seen this before," "how many times does X occur," or "what's the complement/pair for this value," a HashMap turns an O(n²) nested-loop scan into a single O(n) pass. It's the highest-leverage data structure in interviews precisely because it trades a little space for average O(1) lookups on arbitrary keys.
Advertisement