← Daily Logs
LeetCode 2622

Cache With Time Limit

MediumJul 3, 2026cachetimersdesign

A cache where entries expire on their own — `setTimeout` does the eviction, so `get`/`count` never have to check timestamps themselves; they can just trust that an expired key has already deleted itself from the Map.

The problem

Implement `TimeLimitedCache` with `set(key, value, duration)` storing `key: value` for `duration` ms, after which the key becomes inaccessible; it returns `true` if an un-expired `key` already existed (overwriting both its value and duration), `false` otherwise. `get(key)` returns the value for an un-expired key, or `-1`. `count()` returns how many un-expired keys are currently stored.

The tricky part is re-`set`-ting an existing key: the old expiration must not fire late and delete the new value out from under it — the old timer has to be cancelled, not just left to race against a new one on the same key.

The approach

Each cache entry stores a `[value, timeoutId]` pair instead of just the raw value, so the timer that will eventually delete this exact key is always reachable from the entry itself.

`set` always schedules a fresh `setTimeout(() => this.cache.delete(key), duration)` — that's what makes an entry self-expiring, since `get`/`count` never need to know anything about elapsed time. If the key already existed, `hasKey` is captured before the entry is overwritten, and the *previous* entry's timeout is explicitly cancelled with `clearTimeout` — without that, the old timer would still fire after the first call's `duration` and delete the key even though the second `set` gave it a fresh, later expiration.

`get` and `count` stay trivial because expiration is enforced entirely by the timers: an expired key is simply a key that's no longer in the `Map`, so `get` is a plain lookup and `count` is just `this.cache.size`.

The solution

js
var TimeLimitedCache = function() {
  this.cache = new Map();
};

/**
 * @param {number} key
 * @param {number} value
 * @param {number} duration time until expiration in ms
 * @return {boolean} if un-expired key already existed
 */
TimeLimitedCache.prototype.set = function(key, value, duration) {
  const hasKey = this.cache.has(key);
  const timeout = setTimeout(() => {
    this.cache.delete(key);
  }, duration);
  if (hasKey) {
    clearTimeout(this.cache.get(key)[1]);
  }
  this.cache.set(key, [value, timeout]);
  return hasKey;
};

/**
 * @param {number} key
 * @return {number} value associated with key
 */
TimeLimitedCache.prototype.get = function(key) {
  if (this.cache.has(key)) return this.cache.get(key)[0];
  return -1;
};

/**
 * @return {number} count of non-expired keys
 */
TimeLimitedCache.prototype.count = function() {
  return this.cache.size;
};

Time O(1)Space O(n)

All entries