FossilsðŸ’ŧ CodingImplement Memoize
ðŸĢHatchlingJavaScriptPerformanceFunctional

Implement Memoize

Memoization is caching for functions. Your implementation reveals understanding of closures, Map vs WeakMap, cache invalidation, and the trade-offs of caching.

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 growth
  • keyResolver — 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