← run

js-03-lru-cache

0.000
0/1 tests· data-structures
Challenge · difficulty 3/5
# LRU cache

Implement an ES module **`solution.js`** exporting a least-recently-used cache class:

```js
export class LRUCache {
  constructor(capacity) { /* ... */ }
  get(key) { /* ... */ }
  put(key, value) { /* ... */ }
}
```

`capacity` is a positive integer — the maximum number of entries the cache holds.

- **`get(key)`** returns the stored value, or `undefined` if the key is not present. A successful
  `get` counts as a use: it marks the key as the **most recently used**.
- **`put(key, value)`** inserts or updates the entry and marks it most recently used. If adding a
  **new** key would exceed `capacity`, evict the **least recently used** entry first. Updating an
  existing key never changes the number of entries (and refreshes its recency).

"Recently used" is updated by **both** `get` and `put`. Keys may be any value usable as a `Map` key.

Example:
```js
const c = new LRUCache(2);
c.put("a", 1);
c.put("b", 2);
c.get("a");       // => 1   (now "a" is most-recently-used, "b" is least)
c.put("c", 3);    // capacity exceeded -> evicts "b"
c.get("b");       // => undefined
c.get("a");       // => 1
c.get("c");       // => 3
```
tests/solution.test.js
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { LRUCache } from "./solution.js";

test("stores and retrieves a value", () => {
  const c = new LRUCache(2);
  c.put("a", 1);
  assert.equal(c.get("a"), 1);
});

test("missing key returns undefined", () => {
  const c = new LRUCache(2);
  assert.equal(c.get("nope"), undefined);
});

test("evicts the least-recently-used entry at capacity", () => {
  const c = new LRUCache(2);
  c.put("a", 1);
  c.put("b", 2);
  c.put("c", 3); // exceeds capacity -> "a" (oldest) evicted
  assert.equal(c.get("a"), undefined);
  assert.equal(c.get("b"), 2);
  assert.equal(c.get("c"), 3);
});

test("get refreshes recency so a different key is evicted", () => {
  const c = new LRUCache(2);
  c.put("a", 1);
  c.put("b", 2);
  c.get("a"); // "a" now most-recently-used, "b" least
  c.put("c", 3); // evicts "b"
  assert.equal(c.get("b"), undefined);
  assert.equal(c.get("a"), 1);
  assert.equal(c.get("c"), 3);
});

test("updating an existing key does not grow size and refreshes recency", () => {
  const c = new LRUCache(2);
  c.put("a", 1);
  c.put("b", 2);
  c.put("a", 10); // update -> "a" most-recently-used, size stays 2
  c.put("c", 3); // evicts "b", not "a"
  assert.equal(c.get("b"), undefined);
  assert.equal(c.get("a"), 10);
  assert.equal(c.get("c"), 3);
});

test("capacity of 1 keeps only the latest entry", () => {
  const c = new LRUCache(1);
  c.put("a", 1);
  c.put("b", 2);
  assert.equal(c.get("a"), undefined);
  assert.equal(c.get("b"), 2);
});
Proposed solution
**`a`** 1
**`b`** 2
**`c`** 3
**`d`** 4
**`e`** 5
**`f`** 6
**`g`** 7
**`h`** 8
**`i`** 9
**`j`** 10
**`l`** 11
**`o`** 12
**`p`** 13
**`q`** 14
**`r`** 15
**`s`** 16
**`t`** 17
**`x`** 18
**`y`** 19
**`z`** 20
***`a`** 21
***`b`** 22
***`c`** 23
***`d`** 24
***`e`** 25
***`f`** 26
***`g`** 27
***`h`** 28
***`i`** 29
***`j`** 30
***`l`** 31
***`o`** 12
***`p`** 13
***`q`** 14
***`r`** 15
***`s`** 16
***`t`** 17
***`x`** 18
***`y`** 19
***`z`** 20
***`a`** 21
***`b`** 22
***`c`** 23
***`d`** 24
***`e`** 25
***`f`** 26
***`g`** 27
***`h`** 28
***`i`** 29
***`j`** 30
***`l`** 31
***`o`** 12
***`p`** 13
***`q`** 14
***`r`** 15
***`s`** 16
***`t`** 17
***`x`** 18
***`y`** 19
***`z`** 20
***`a`** 21
***`b`** 22
***`c`** 23
***`d`** 24
***`e`** 25
***`f`** 26
***`g`** 27
***`h`** 28
***`i`** 29
***`j`** 30
***`l`** 31
***`o`** 12
***`p`** 13
***`q`** 14
***`r`** 15
***`s`** 16
***`t`** 17
***`x`** 18
***`y`** 19
***`z`** 20
***`a`** 21
***`b`** 22
***`c`** 23
***`d`** 24
***`e`** 25
***`f`** 26
***`g`** 27
***`h`** 28
***`i`** 29
***`j`** 30

Errors (stderr)

no code extracted from response