Skip to the code
cf.

LRU cache

Write a class LRUCache. new LRUCache(capacity) makes an empty cache that holds at most capacity entries, where capacity is a positive integer. Values can be anything, including 0, "", and false.

get(key) returns the value stored under key, or undefined if there's none, and counts as a use of that key.

put(key, value) adds an entry, or updates the value if the key is already there, and also counts as a use. When adding a key puts the cache over capacity, drop the least recently used entry: the one whose last use is the oldest.

size is a getter for the number of entries.

Both get and put should take O(1) time.

const cache = new LRUCache(2);
cache.put("a", 1);
cache.put("b", 2);
cache.get("a");    // 1, so "b" is now the least recently used
cache.put("c", 3); // over capacity: drops "b"
cache.get("b");    // undefined
cache.size;        // 2

What the tests check

  1. returns stored values, and undefined for missing keys
    const cache = new LRUCache(2);
    cache.put("a", 1);
    expect(cache.get("a")).toBe(1);
    expect(cache.get("z")).toBeUndefined();
    
  2. drops the oldest key when it goes over capacity
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("c", 3);
    expect(cache.get("a")).toBeUndefined();
    expect(cache.get("b")).toBe(2);
    expect(cache.get("c")).toBe(3);
    
  3. get counts as a use
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.get("a");
    cache.put("c", 3);
    expect(cache.get("b")).toBeUndefined();
    expect(cache.get("a")).toBe(1);
    
  4. updating a key counts as a use
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("a", 10);
    cache.put("c", 3);
    expect(cache.get("a")).toBe(10);
    expect(cache.get("b")).toBeUndefined();
    
  5. updating a key in a full cache drops nothing
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.put("b", 20);
    expect(cache.size).toBe(2);
    expect(cache.get("a")).toBe(1);
    expect(cache.get("b")).toBe(20);
    
  6. size counts the entries and never goes over capacity
    const cache = new LRUCache(3);
    expect(cache.size).toBe(0);
    cache.put("a", 1);
    cache.put("b", 2);
    expect(cache.size).toBe(2);
    cache.put("c", 3);
    cache.put("d", 4);
    cache.put("e", 5);
    expect(cache.size).toBe(3);
    
  7. a hit on a falsy value still counts as a use
    const cache = new LRUCache(2);
    cache.put("zero", 0);
    cache.put("b", 2);
    expect(cache.get("zero")).toBe(0);
    cache.put("c", 3);
    expect(cache.get("zero")).toBe(0);
    expect(cache.get("b")).toBeUndefined();
    
  8. a get for a missing key adds nothing
    const cache = new LRUCache(2);
    cache.put("a", 1);
    cache.put("b", 2);
    cache.get("missing");
    expect(cache.size).toBe(2);
    expect(cache.get("a")).toBe(1);
    expect(cache.get("b")).toBe(2);
    

Learn more

solution.jsSaved in this browser

Run tests runs every test on your code, here in your browser. The problem is solved when they all pass.

Ctrl + Enter runs the tests (⌘ + Enter on a Mac).