Least-Recently-Used Cache
A Node module implementing an LRU cache with a fixed capacity, get and set with eviction, and hit and miss statistics.
Project
node-lru-cache
Entry point: lru.mjs
Install
node --versionRun
node lru.mjsTest
node --test lru.test.mjsExpected output
Prints cache hits, misses, and the key that was evicted when capacity was exceeded.Dependencies
- node >=20
What it does
Implements an LRU cache where reads refresh recency, using the insertion order of a Map to make eviction O(1).
export class LruCache {
constructor(capacity) {
if (!Number.isInteger(capacity) || capacity < 1) throw new Error('capacity must be >= 1');
this.capacity = capacity;
this.map = new Map();
this.hits = 0;
this.misses = 0;
}
get(key) {
if (!this.map.has(key)) { this.misses++; return undefined; }
const value = this.map.get(key);
this.map.delete(key);
this.map.set(key, value);
this.hits++;
return value;
}
set(key, value) {
if (this.map.has(key)) this.map.delete(key);
this.map.set(key, value);
if (this.map.size > this.capacity) {
const oldest = this.map.keys().next().value;
this.map.delete(oldest);
return oldest;
}
return null;
}
}
if (import.meta.url === `file://${process.argv[1]}`) {
const cache = new LruCache(2);
cache.set('a', 1);
cache.set('b', 2);
cache.get('a'); // refresh a
const evicted = cache.set('c', 3); // evicts b
console.log('evicted=' + evicted);
console.log('hits=' + cache.hits + ' misses=' + cache.misses);
}
Statistics are tracked so the demo can show why a cache is working: a high hit rate means the capacity and key choice are doing their job.
Run and verify
node lru.mjs
Run the module directly to execute the demo; importing it as a library skips the demo block.
Expected Output
The demo touches a to refresh it and then inserts c, which should evict b. The console.log output is:
evicted=b
hits=1 misses=0
Reading an existing key counts as a hit and moves it to most-recent, so the next insert evicts the least-recently used key rather than the oldest inserted.
Limitations
The cache holds values in memory and has no time-based expiry; add a timestamp and a TTL check if entries must age out.