ts-03-lru-cache
0.000
Challenge · difficulty 3/5
# Generic LRU cache
Implement **`solution.ts`** exporting a generic class:
```ts
export class LRUCache<K, V> {
constructor(capacity: number);
get(key: K): V | undefined;
put(key: K, value: V): void;
get size(): number;
}
```
A least-recently-used cache holding at most `capacity` entries.
- `get(key)` returns the stored value, or `undefined` if absent. A successful `get`
counts as a **use** (it makes that key the most-recently used).
- `put(key, value)` inserts or updates a key. Updating an existing key also counts as a
use. When inserting a **new** key would exceed `capacity`, evict the
least-recently-used key first.
- `size` is the current number of entries (never exceeds `capacity`).
- Throw a `RangeError` if `capacity < 1`.
Example:
```ts
const c = new LRUCache<string, number>(2);
c.put("a", 1);
c.put("b", 2);
c.get("a"); // 1 -> "a" is now most-recently used
c.put("c", 3); // evicts "b" (least-recently used)
c.get("b"); // undefined
c.get("a"); // 1
c.get("c"); // 3
c.size; // 2
```
Keep it fully typed (must pass `tsc --noEmit` in strict mode). Do not use `any` in the
public API.
tests/solution.test.ts
import { test } from "node:test";
import { strict as assert } from "node:assert";
import { LRUCache } from "./solution.ts";
test("stores and retrieves values", () => {
const c = new LRUCache<string, number>(2);
c.put("a", 1);
c.put("b", 2);
assert.equal(c.get("a"), 1);
assert.equal(c.get("b"), 2);
assert.equal(c.size, 2);
});
test("missing key returns undefined", () => {
const c = new LRUCache<string, number>(2);
assert.equal(c.get("nope"), undefined);
});
test("evicts least-recently-used on overflow", () => {
const c = new LRUCache<string, number>(2);
c.put("a", 1);
c.put("b", 2);
c.put("c", 3); // evicts "a"
assert.equal(c.get("a"), undefined);
assert.equal(c.get("b"), 2);
assert.equal(c.get("c"), 3);
assert.equal(c.size, 2);
});
test("get counts as a use and protects from eviction", () => {
const c = new LRUCache<string, number>(2);
c.put("a", 1);
c.put("b", 2);
assert.equal(c.get("a"), 1); // "a" now most-recently used
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("put updates existing key and marks it used", () => {
const c = new LRUCache<string, number>(2);
c.put("a", 1);
c.put("b", 2);
c.put("a", 10); // update + use "a"
c.put("c", 3); // evicts "b"
assert.equal(c.get("a"), 10);
assert.equal(c.get("b"), undefined);
assert.equal(c.get("c"), 3);
assert.equal(c.size, 2);
});
test("capacity of 1 keeps only the newest", () => {
const c = new LRUCache<number, string>(1);
c.put(1, "one");
c.put(2, "two");
assert.equal(c.get(1), undefined);
assert.equal(c.get(2), "two");
assert.equal(c.size, 1);
});
test("invalid capacity throws RangeError", () => {
assert.throws(() => new LRUCache<string, number>(0), RangeError);
});
Proposed solution
# Generic LRU cache
Implement **`solution.ts`** exporting a generic class:
```ts
export class LRUCache<K, V> {
constructor(capacity: number);
get key: K | void | undefined;
put key: K | void |;
size: number |;
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
get key: K | void |;
put key: K | void |;
size: number |;
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
get key: K | void |;
put key: K | void |;
size: number |;
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
}
```
Implement **`solution.ts`** exporting a generic class:
```js
export class LRUCache<K, V> {
constructor(capacity: number);
}
```
Implement **`solution.ts`** exporting a generic class:
```js
exportTest output (stdout)
TAP version 13
# node:internal/modules/run_main:107
# triggerUncaughtException(
# ^
# Error: Transform failed with 1 error:
# /tmp/llmlab-ts-03-lru-cache-s8gs82mh/solution.ts:3:9: ERROR: Expected "(" but found ":"
# at failureErrorWithLog (/home/psc/opt/node/lib/node_modules/tsx/node_modules/esbuild/lib/main.js:1748:15)
# at /home/psc/opt/node/lib/node_modules/tsx/node_modules/esbuild/lib/main.js:1017:50
# at responseCallbacks.<computed> (/home/psc/opt/node/lib/node_modules/tsx/node_modules/esbuild/lib/main.js:884:9)
# at handleIncomingPacket (/home/psc/opt/node/lib/node_modules/tsx/node_modules/esbuild/lib/main.js:939:12)
# at Socket.readFromStdout (/home/psc/opt/node/lib/node_modules/tsx/node_modules/esbuild/lib/main.js:862:7)
# at Socket.emit (node:events:509:28)
# at addChunk (node:internal/streams/readable:563:12)
# at readableAddChunkPushByteMode (node:internal/streams/readable:514:3)
# at Readable.push (node:internal/streams/readable:394:5)
# at Pipe.onStreamRead (node:internal/stream_base_commons:189:23) {
# name: 'TransformError'
# }
# Node.js v24.16.0
# Subtest: solution.test.ts
not ok 1 - solution.test.ts
---
duration_ms: 63.893194
type: 'test'
location: '/tmp/llmlab-ts-03-lru-cache-s8gs82mh/solution.test.ts:1:1'
failureType: 'testCodeFailure'
exitCode: 1
signal: ~
error: 'test failed'
code: 'ERR_TEST_FAILURE'
...
1..1
# tests 1
# suites 0
# pass 0
# fail 1
# cancelled 0
# skipped 0
# todo 0
# duration_ms 67.6455