Skip to content

Commit

Permalink
Make QuickLRU a Map subclass (sindresorhus#37)
Browse files Browse the repository at this point in the history
Co-authored-by: Sindre Sorhus <sindresorhus@gmail.com>
  • Loading branch information
Javier Blanco and sindresorhus authored Feb 18, 2022
1 parent cf1bbcf commit 2dd5b52
Show file tree
Hide file tree
Showing 4 changed files with 94 additions and 2 deletions.
2 changes: 1 addition & 1 deletion index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ export interface Options<KeyType, ValueType> {
onEviction?: (key: KeyType, value: ValueType) => void;
}

export default class QuickLRU<KeyType, ValueType> implements Iterable<[KeyType, ValueType]> {
export default class QuickLRU<KeyType, ValueType> extends Map implements Iterable<[KeyType, ValueType]> {
/**
The stored item count.
*/
Expand Down
18 changes: 17 additions & 1 deletion index.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
export default class QuickLRU {
export default class QuickLRU extends Map {
constructor(options = {}) {
super();

if (!(options.maxSize && options.maxSize > 0)) {
throw new TypeError('`maxSize` must be a number greater than 0');
}
Expand Down Expand Up @@ -262,4 +264,18 @@ export default class QuickLRU {

return Math.min(this._size + oldCacheSize, this.maxSize);
}

entries() {
return this.entriesAscending();
}

forEach(callbackFunction, thisArgument = this) {
for (const [key, value] of this.entriesAscending()) {
callbackFunction.call(thisArgument, value, key, this);
}
}

[Symbol.toStringTag]() {
return JSON.stringify([...this.entriesAscending()]);
}
}
14 changes: 14 additions & 0 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ lru.get('🦄');

Returns a new instance.

It's a [`Map`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) subclass.

### options

Type: `object`
Expand Down Expand Up @@ -122,6 +124,18 @@ Iterable for all entries, starting with the oldest (ascending in recency).

Iterable for all entries, starting with the newest (descending in recency).

#### .entries()

Iterable for all entries, starting with the newest (ascending in recency).

**This method exists for `Map` compatibility. Prefer [.entriesAscending()](#entriesascending) instead.**

#### .forEach(callbackFunction, thisArgument)

Loop over entries calling the `callbackFunction` for each entry (ascending in recency).

**This method exists for `Map` compatibility. Prefer [.entriesAscending()](#entriesascending) instead.**

#### .size

The stored item count.
Expand Down
62 changes: 62 additions & 0 deletions test.js
Original file line number Diff line number Diff line change
Expand Up @@ -577,6 +577,35 @@ test('max age - `entriesAscending()` should return the entries that are not expi
t.deepEqual([...lru.entriesAscending()], [['3', 'test3'], ['4', 'coco'], ['5', 'loco']]);
});

test('max age - `entries()` should return the entries that are not expired', async t => {
const lru = new QuickLRU({maxSize: 10, maxAge: 100});
lru.set('1', undefined);
lru.set('2', 'test2');
await delay(200);
lru.set('3', 'test3');
lru.set('4', 'coco');
lru.set('5', 'loco');

t.deepEqual([...lru.entries()], [['3', 'test3'], ['4', 'coco'], ['5', 'loco']]);
});

test('max age - `forEach()` should not return expired entries', async t => {
const lru = new QuickLRU({maxSize: 5, maxAge: 100});
lru.set('1', undefined);
lru.set('2', 'test2');
lru.set('3', 'test3');
await delay(200);
lru.set('4', 'coco');
lru.set('5', 'loco');
const entries = [];

lru.forEach((value, key) => {
entries.push([key, value]);
});

t.deepEqual(entries, [['4', 'coco'], ['5', 'loco']]);
});

test('max age - `.[Symbol.iterator]()` should not return expired items', async t => {
const lru = new QuickLRU({maxSize: 2, maxAge: 100});
lru.set('key', 'value');
Expand Down Expand Up @@ -617,6 +646,32 @@ test('entriesDescending enumerates cache items newest-first', t => {
t.deepEqual([...lru.entriesDescending()], [['v', 3], ['t', 4], ['a', 8], ['q', 2]]);
});

test('entries enumerates cache items oldest-first', t => {
const lru = new QuickLRU({maxSize: 3});
lru.set('1', 1);
lru.set('2', 2);
lru.set('3', 3);
lru.set('3', 7);
lru.set('2', 8);
t.deepEqual([...lru.entries()], [['1', 1], ['3', 7], ['2', 8]]);
});

test('forEach calls the cb function for each cache item oldest-first', t => {
const lru = new QuickLRU({maxSize: 3});
lru.set('1', 1);
lru.set('2', 2);
lru.set('3', 3);
lru.set('3', 7);
lru.set('2', 8);
const entries = [];

lru.forEach((value, key) => {
entries.push([key, value]);
});

t.deepEqual(entries, [['1', 1], ['3', 7], ['2', 8]]);
});

test('resize removes older items', t => {
const lru = new QuickLRU({maxSize: 2});
lru.set('1', 1);
Expand Down Expand Up @@ -685,3 +740,10 @@ test('function value', t => {
lru.get('fn')();
t.true(isCalled);
});

test('[Symbol.toStringTag] converts the cache items to a string in ascending order', t => {
const lru = new QuickLRU({maxSize: 2});
lru.set('1', 1);
lru.set('2', 2);
t.is(lru[Symbol.toStringTag](), '[["1",1],["2",2]]');
});

0 comments on commit 2dd5b52

Please sign in to comment.