Implement Memoize
Interview Question: "Implement a memoize function that caches the results of expensive function calls."
Level 1: Single Argument
function memoize(fn) {
const cache = new Map();
return function(arg) {
if (cache.has(arg)) return cache.get(arg);
const result = fn.call(this, arg);
cache.set(arg, result);
return result;
};
}Level 2: Multiple Arguments
function memoize(fn, keyResolver) {
const cache = new Map();
return function(...args) {
const key = keyResolver ? keyResolver(...args) : JSON.stringify(args);
if (cache.has(key)) return cache.get(key);
const result = fn.apply(this, args);
cache.set(key, result);
return result;
};
}
// Usage
const expensiveCalc = memoize((a, b, c) => {
console.log('Computing...');
return a * b + c;
});
expensiveCalc(2, 3, 4); // "Computing..." â 10
expensiveCalc(2, 3, 4); // â 10 (cached, no log)Level 3: With Max Size (LRU Eviction)
function memoize(fn, { maxSize = 100, keyResolver } = {}) {
const cache = new Map();
const memoized = function(...args) {
const key = keyResolver ? keyResolver(...args) : JSON.stringify(args);
if (cache.has(key)) {
const value = cache.get(key);
cache.delete(key);
cache.set(key, value);
return value;
}
const result = fn.apply(this, args);
cache.set(key, result);
if (cache.size > maxSize) {
const oldest = cache.keys().next().value;
cache.delete(oldest);
}
return result;
};
memoized.cache = cache;
memoized.clear = () => cache.clear();
return memoized;
}Key points:
- LRU via Map insertion order â Delete and re-insert on access moves to end
maxSizeâ Prevents unbounded memory growthkeyResolverâ Custom key generation for complex arguments- Exposed
cacheâ Allows inspection and manual invalidation
Follow-Up: "When should you NOT memoize?"
- Functions with side effects (network calls, DOM mutations)
- Functions with non-deterministic results (random, time-based)
- Functions called with many unique inputs (cache grows forever, no hits)
- Simple functions where computation is cheaper than cache lookup