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..7172f746 --- /dev/null +++ b/test/data-structures/testLRUCache.js @@ -0,0 +1,48 @@ +/* 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('store.a = 5, expect store.a to be 5', () => { + store.a = 5; + assert.equal(store.a, 5); + }); + 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); + 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); + }); + it('store.delete(\'e\'), expect return to be true, store.size to be 2', () => { + assert.equal(store.delete('e'), true); + assert.equal(store.size, 2); + }); + it('store.cache(\'c\', 4), store.c should be 4', () => { + store.cache('c', 4); + assert.equal(store.c, 4); + }); + it('store.capacity = 1, store.length should be 1, store[0] should be \'c\'', () => { + store.capacity = 1; + assert.equal(Object.keys(store).length, 1); + assert.equal(Object.keys(store)[0], 'c'); + }); +});