From 290c24db96d87a923aefd55a7605d2a283529e9f Mon Sep 17 00:00:00 2001 From: zhengjynicolas Date: Sun, 19 Nov 2023 14:09:32 +0800 Subject: [PATCH 1/2] Add LRUCache, along with test cases --- src/data-structures/LRU_cache.js | 65 ++++++++++++++++++++++++++++ src/data-structures/index.js | 4 +- test/data-structures/testLRUCache.js | 57 ++++++++++++++++++++++++ 3 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/data-structures/LRU_cache.js create mode 100644 test/data-structures/testLRUCache.js diff --git a/src/data-structures/LRU_cache.js b/src/data-structures/LRU_cache.js new file mode 100644 index 00000000..b2860a19 --- /dev/null +++ b/src/data-structures/LRU_cache.js @@ -0,0 +1,65 @@ +class LRUCache { + constructor(capacity, values) { + const _cache = new Map(); + const leastRecent = () => _cache.keys().next().value; + // getter + const get = key => () => { + const value = _cache.get(key); + if (_cache.has(key)) { + // refresh key + _cache.delete(key); + _cache.set(key, value); + } + return value; + }; + // setter + const set = key => (value) => { + if (_cache.has(key)) { + // refresh key + _cache.delete(key); + } else if (_cache.size === capacity) { + this.delete(leastRecent()); + } + _cache.set(key, value); + }; + + Object.defineProperties(this, { + cache: { + value: (key, value) => { + // update key value + set(key)(value); + return key in this ? this : Object.defineProperty(this, key, { + get: get(key), set: set(key), enumerable: true, configurable: true + }); + } + }, + delete: { + value: (key) => { + _cache.delete(key); + try { + return delete this[key]; + } catch (e) { + return false; + } + } + }, + size: { + get: () => _cache.size + }, + capacity: { + get: () => capacity, + set: (value) => { + capacity = value; + while (this.size > capacity) { + // delete least recent used keys + this.delete(leastRecent()); + } + } + }, + }); + + Object.entries(values).forEach(([key, value]) => this.cache(key, value)); + } +} + +module.exports = LRUCache; diff --git a/src/data-structures/index.js b/src/data-structures/index.js index 742a4b06..da87a1ea 100644 --- a/src/data-structures/index.js +++ b/src/data-structures/index.js @@ -6,6 +6,7 @@ const LinkedList = require('./linked_list'); const Queue = require('./queue'); const Stack = require('./stack'); const Trie = require('./trie'); +const LRUCache = require('./LRU_cache'); module.exports = { DoublyLinkedList, @@ -15,5 +16,6 @@ module.exports = { LinkedList, Queue, Stack, - Trie + Trie, + LRUCache }; diff --git a/test/data-structures/testLRUCache.js b/test/data-structures/testLRUCache.js new file mode 100644 index 00000000..81a99cba --- /dev/null +++ b/test/data-structures/testLRUCache.js @@ -0,0 +1,57 @@ +/* eslint-env mocha */ +const LRUCache = require('../../src').datastructures.LRUCache; +const assert = require('assert'); + +describe('assert LRUCache', () => { + let store; + it('initialized size to be 1, capacity to be 3, store.a should be 1', ()=>{ + store = new LRUCache(3, {a: 1}); + assert.equal(store.size, 1, 'store.size'); + assert.equal(store.capacity, 3, 'store.capacity'); + assert.equal(store.a, 1); + }); + it('store.cache(\'b\', 2), expect store[\'b\'] to be 2', () => { + assert.equal(store.cache('b', 2)['b'], 2); + }); + it('set store.a = 5, expect store.a to be 5', () => { + store.a = 5; + assert.equal(store.a, 5); + }); + it('store.b to be undefined, store.c to be 3, store.d to be 4, store.a to be 5, store.d to be 4 store.size to be 3', () => { + store.cache('c', 3).cache('d', 4); + assert.equal(store.b, undefined, 'store.b'); + assert.equal(store.c, 3, 'store.c'); + assert.equal(store.a, 5, 'store.a'); + assert.equal(store.d, 4, 'store.d'); + assert.equal(store.size, 3, 'store.size'); + }); + it('store.delete(\'delete\', expect return to be false)', () => { + assert.equal(store.delete('delete'), false); + }); + it('store.delete(\'d\'), expect return to be true, store.d to be undefined', () => { + assert.equal(store.delete('d'), true); + assert.equal(store.d, undefined, 'store.d'); + }); + it('store.delete(\'e\'), expect return to be true, store.size to be 2', () => { + assert.equal(store.delete('e'), true, "store.delete('e')"); + assert.equal(store.size, 2, 'store.size'); + }); + it('store.c should be 4', () => { + store.cache('c', 4); + assert.equal(store.c, 4); + }); + it('store.c should be 4', () => { + store.cache('c', 4); + assert.equal(store.c, 4); + }); + it('store.length should be 1', () => { + store.capacity = 1; + assert.equal(Object.keys(store).length, 1); + }); + it('store.length should be 1, store[0] should be "c"', () => { + assert.equal(Object.keys(store).length, 1); + }); + it('store[0] should be "c"', () => { + assert.equal(Object.keys(store)[0], 'c'); + }); +}); From 25dc8b14bf22cc5974ec75bbc0202b04151f2d7f Mon Sep 17 00:00:00 2001 From: zhengjynicolas Date: Sun, 19 Nov 2023 14:44:27 +0800 Subject: [PATCH 2/2] update test cases for LRUCache --- test/data-structures/testLRUCache.js | 33 ++++++++++------------------ 1 file changed, 12 insertions(+), 21 deletions(-) diff --git a/test/data-structures/testLRUCache.js b/test/data-structures/testLRUCache.js index 81a99cba..7172f746 100644 --- a/test/data-structures/testLRUCache.js +++ b/test/data-structures/testLRUCache.js @@ -13,45 +13,36 @@ describe('assert LRUCache', () => { it('store.cache(\'b\', 2), expect store[\'b\'] to be 2', () => { assert.equal(store.cache('b', 2)['b'], 2); }); - it('set store.a = 5, expect store.a to be 5', () => { + it('store.a = 5, expect store.a to be 5', () => { store.a = 5; assert.equal(store.a, 5); }); - it('store.b to be undefined, store.c to be 3, store.d to be 4, store.a to be 5, store.d to be 4 store.size to be 3', () => { + it('store.cache(\'c\', 3).cache(\'d\', 4), store.b to be undefined, store.c to be 3, store.d to be 4, store.a to be 5, store.d to be 4 store.size to be 3', () => { store.cache('c', 3).cache('d', 4); - assert.equal(store.b, undefined, 'store.b'); - assert.equal(store.c, 3, 'store.c'); - assert.equal(store.a, 5, 'store.a'); - assert.equal(store.d, 4, 'store.d'); - assert.equal(store.size, 3, 'store.size'); + assert.equal(store.b, undefined); + assert.equal(store.c, 3); + assert.equal(store.a, 5); + assert.equal(store.d, 4); + assert.equal(store.size, 3); }); it('store.delete(\'delete\', expect return to be false)', () => { assert.equal(store.delete('delete'), false); }); it('store.delete(\'d\'), expect return to be true, store.d to be undefined', () => { assert.equal(store.delete('d'), true); - assert.equal(store.d, undefined, 'store.d'); + assert.equal(store.d, undefined); }); it('store.delete(\'e\'), expect return to be true, store.size to be 2', () => { - assert.equal(store.delete('e'), true, "store.delete('e')"); - assert.equal(store.size, 2, 'store.size'); - }); - it('store.c should be 4', () => { - store.cache('c', 4); - assert.equal(store.c, 4); + assert.equal(store.delete('e'), true); + assert.equal(store.size, 2); }); - it('store.c should be 4', () => { + it('store.cache(\'c\', 4), store.c should be 4', () => { store.cache('c', 4); assert.equal(store.c, 4); }); - it('store.length should be 1', () => { + it('store.capacity = 1, store.length should be 1, store[0] should be \'c\'', () => { store.capacity = 1; assert.equal(Object.keys(store).length, 1); - }); - it('store.length should be 1, store[0] should be "c"', () => { - assert.equal(Object.keys(store).length, 1); - }); - it('store[0] should be "c"', () => { assert.equal(Object.keys(store)[0], 'c'); }); });