Implementation
Once a problem is recognized as one of the four buckets from the intuition chapter, the code follows a fixed shape. These are the four skeletons, one per bucket, worth knowing from memory rather than re-derived each time.
Seen Before
function seenBefore(items) {
const seen = new Set();
for (const item of items) {
if (seen.has(item)) return true; // already ran into this one
seen.add(item);
}
return false;
}
Frequency
function frequency(items) {
const counts = new Map();
for (const item of items) {
counts.set(item, (counts.get(item) || 0) + 1);
}
return counts;
}
Pairing
function pairing(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 [];
}
The check happens before the insert, not after. That order matters, it is what stops a value from pairing with itself.
Grouping
function grouping(items, keyFn) {
const groups = new Map();
for (const item of items) {
const key = keyFn(item);
if (!groups.has(key)) groups.set(key, []);
groups.get(key).push(item);
}
return [...groups.values()];
}
keyFn is whatever computed property decides which bucket an item belongs to, a sorted string for anagrams, a row or column index for a grid, whatever the problem defines as “belongs together.”