From 98ffd1d8d01f693ff733098dfa7120455b573be3 Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Sat, 26 Mar 2022 21:20:58 +0600 Subject: [PATCH 1/5] feat: added new validation test casses & methods --- Cache/LRUCache.js | 99 +++++++++++++++++++++++++++++++------ Cache/test/LRUCache.test.js | 43 +++++++++++++--- 2 files changed, 120 insertions(+), 22 deletions(-) diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index e416e03776..01890623db 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -1,39 +1,106 @@ class LRUCache { // LRU Cache to store a given capacity of data - constructor (capacity) { - this.cache = new Map() - this.capacity = capacity + #capacity + + constructor(capacity) { + if (!Number.isInteger(capacity) || capacity < 0) { + throw new TypeError('Invalid capacity') + } + + this.#capacity = ~~capacity + this.misses = 0 this.hits = 0 - this.miss = 0 + this.cache = new Map() + } + + get info() { + return Object.freeze({ + misses: this.misses, + hits: this.hits, + capacity: this.capacity, + size: this.size + }) + } + + get size() { + return this.cache.size + } + + get capacity() { + return this.#capacity + } + + /** + * delete oldest key existing in map by the help of iterator + */ + #removeLeastRecentlyUsed() { + this.cache.delete(this.cache.keys().next().value) + } + + set capacity(newCapacity) { + if (newCapacity < 0) { + throw new RangeError('Capacity should be greater than 0') + } + + if (newCapacity < this.capacity) { + let diff = this.capacity - newCapacity + + while (diff--) { + this.#removeLeastRecentlyUsed() + } + } + + this.#capacity = newCapacity } - cacheInfo () { - // Return the details for the cache instance [hits, misses, capacity, current_size] - return `CacheInfo(hits=${this.hits}, misses=${this.miss}, capacity=${this.capacity}, current size=${this.cache.size})` + has(key) { + key = String(key) + + return this.cache.has(key) } - set (key, value) { + set(key, value) { + key = String(key) // Sets the value for the input key and if the key exists it updates the existing key - if (this.cache.size === this.capacity) { - // delete oldest key existing in map - this.cache.delete(this.cache.keys().next().value) + if (this.size === this.capacity) { + this.#removeLeastRecentlyUsed() } + this.cache.set(key, value) } - get (key) { + get(key) { + key = String(key) // Returns the value for the input key. Returns null if key is not present in cache if (this.cache.has(key)) { const value = this.cache.get(key) + // refresh the cache to update the order of key this.cache.delete(key) this.cache.set(key, value) - this.hits += 1 + + this.hits++ return value - } else { - this.miss += 1 - return null } + + this.misses++ + return null + } + + toString(indent) { + const replacer = (_, value) => { + if (value instanceof Set) { + return [...value] + } + + if (value instanceof Map) { + return Object.fromEntries(value) + } + + return value + } + + return JSON.stringify(this, replacer, indent) } } diff --git a/Cache/test/LRUCache.test.js b/Cache/test/LRUCache.test.js index b03d609d66..cd409f258e 100644 --- a/Cache/test/LRUCache.test.js +++ b/Cache/test/LRUCache.test.js @@ -1,9 +1,19 @@ import { LRUCache } from '../LRUCache' import { fibonacciCache } from './cacheTest' -describe('LRUCache', () => { - it('Example 1 (Small Cache, size=2)', () => { - const cache = new LRUCache(2) +describe('Testing LRUCache', () => { + it("Testing with invalid capacity", () => { + expect(() => new LRUCache()).toThrow() + expect(() => new LRUCache('Invalid')).toThrow() + expect(() => new LRUCache(-1)).toThrow() + expect(() => new LRUCache(Infinity)).toThrow() + }) + + it('Example 1 (Small Cache, size = 2)', () => { + const cache = new LRUCache(1) // initially capacity + + cache.capacity++ // now the capacity is increasing by one + cache.set(1, 1) cache.set(2, 2) @@ -24,14 +34,35 @@ describe('LRUCache', () => { expect(cache.get(3)).toBe(3) expect(cache.get(4)).toBe(4) - expect(cache.cacheInfo()).toBe('CacheInfo(hits=6, misses=3, capacity=2, current size=2)') + expect(cache.info).toEqual({ + misses: 3, + hits: 6, + capacity: 2, + size: 2 + }) + + cache.capacity-- // now the capacity decreasing by one + + expect(cache.info).toEqual({ + misses: 3, + hits: 6, + capacity: 1, + size: 1 + }) }) - it('Example 2 (Computing Fibonacci Series, size=100)', () => { + it('Example 2 (Computing Fibonacci Series, size = 100)', () => { const cache = new LRUCache(100) + for (let i = 1; i <= 100; i++) { fibonacciCache(i, cache) } - expect(cache.cacheInfo()).toBe('CacheInfo(hits=193, misses=103, capacity=100, current size=98)') + + expect(cache.info).toEqual({ + misses: 103, + hits: 193, + capacity: 100, + size: 98 + }) }) }) From b8d6e686deb01499ac2d0bebd4bc022ff097c74a Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Sat, 26 Mar 2022 21:27:00 +0600 Subject: [PATCH 2/5] style: formated with standard --- Cache/LRUCache.js | 20 ++++++++++---------- Cache/test/LRUCache.test.js | 6 +++--- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index 01890623db..ae62ec33e5 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -2,7 +2,7 @@ class LRUCache { // LRU Cache to store a given capacity of data #capacity - constructor(capacity) { + constructor (capacity) { if (!Number.isInteger(capacity) || capacity < 0) { throw new TypeError('Invalid capacity') } @@ -13,7 +13,7 @@ class LRUCache { this.cache = new Map() } - get info() { + get info () { return Object.freeze({ misses: this.misses, hits: this.hits, @@ -22,22 +22,22 @@ class LRUCache { }) } - get size() { + get size () { return this.cache.size } - get capacity() { + get capacity () { return this.#capacity } /** * delete oldest key existing in map by the help of iterator */ - #removeLeastRecentlyUsed() { + #removeLeastRecentlyUsed () { this.cache.delete(this.cache.keys().next().value) } - set capacity(newCapacity) { + set capacity (newCapacity) { if (newCapacity < 0) { throw new RangeError('Capacity should be greater than 0') } @@ -53,13 +53,13 @@ class LRUCache { this.#capacity = newCapacity } - has(key) { + has (key) { key = String(key) return this.cache.has(key) } - set(key, value) { + set (key, value) { key = String(key) // Sets the value for the input key and if the key exists it updates the existing key if (this.size === this.capacity) { @@ -69,7 +69,7 @@ class LRUCache { this.cache.set(key, value) } - get(key) { + get (key) { key = String(key) // Returns the value for the input key. Returns null if key is not present in cache if (this.cache.has(key)) { @@ -87,7 +87,7 @@ class LRUCache { return null } - toString(indent) { + toString (indent) { const replacer = (_, value) => { if (value instanceof Set) { return [...value] diff --git a/Cache/test/LRUCache.test.js b/Cache/test/LRUCache.test.js index cd409f258e..c78ed70962 100644 --- a/Cache/test/LRUCache.test.js +++ b/Cache/test/LRUCache.test.js @@ -2,7 +2,7 @@ import { LRUCache } from '../LRUCache' import { fibonacciCache } from './cacheTest' describe('Testing LRUCache', () => { - it("Testing with invalid capacity", () => { + it('Testing with invalid capacity', () => { expect(() => new LRUCache()).toThrow() expect(() => new LRUCache('Invalid')).toThrow() expect(() => new LRUCache(-1)).toThrow() @@ -10,7 +10,7 @@ describe('Testing LRUCache', () => { }) it('Example 1 (Small Cache, size = 2)', () => { - const cache = new LRUCache(1) // initially capacity + const cache = new LRUCache(1) // initially capacity cache.capacity++ // now the capacity is increasing by one @@ -57,7 +57,7 @@ describe('Testing LRUCache', () => { for (let i = 1; i <= 100; i++) { fibonacciCache(i, cache) } - + expect(cache.info).toEqual({ misses: 103, hits: 193, From b7c961cc82e110eedfd6160bae25c57ec342886c Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Sat, 26 Mar 2022 21:41:43 +0600 Subject: [PATCH 3/5] feat: added parse method & test cases --- Cache/LRUCache.js | 13 +++++++++++++ Cache/test/LRUCache.test.js | 10 ++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index ae62ec33e5..46f1d5b418 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -87,6 +87,19 @@ class LRUCache { return null } + parse (json) { + const { misses, hits, cache } = JSON.parse(json) + + this.misses += misses ?? 0 + this.hits += hits ?? 0 + + for (const key in cache) { + this.set(key, cache[key]) + } + + return this + } + toString (indent) { const replacer = (_, value) => { if (value instanceof Set) { diff --git a/Cache/test/LRUCache.test.js b/Cache/test/LRUCache.test.js index c78ed70962..d340dac010 100644 --- a/Cache/test/LRUCache.test.js +++ b/Cache/test/LRUCache.test.js @@ -41,11 +41,17 @@ describe('Testing LRUCache', () => { size: 2 }) + const json = '{"misses":3,"hits":6,"cache":{"3":3,"4":4}}' + expect(cache.toString()).toBe(json) + + // merge with json + cache.parse(json) + cache.capacity-- // now the capacity decreasing by one expect(cache.info).toEqual({ - misses: 3, - hits: 6, + misses: 6, + hits: 12, capacity: 1, size: 1 }) From 6b7b715696f2715b66fd626f9b9dcdcc570f21e4 Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Sun, 27 Mar 2022 06:52:48 +0600 Subject: [PATCH 4/5] docs: added js docs --- Cache/LRUCache.js | 40 +++++++++++++++++++++++++++++++++------- 1 file changed, 33 insertions(+), 7 deletions(-) diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index 46f1d5b418..71022ff56a 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -2,6 +2,10 @@ class LRUCache { // LRU Cache to store a given capacity of data #capacity + /** + * @param {number} capacity - the capacity of LRUCache + * @returns {LRUCache} - sealed + */ constructor (capacity) { if (!Number.isInteger(capacity) || capacity < 0) { throw new TypeError('Invalid capacity') @@ -11,6 +15,8 @@ class LRUCache { this.misses = 0 this.hits = 0 this.cache = new Map() + + return Object.seal(this) } get info () { @@ -30,13 +36,6 @@ class LRUCache { return this.#capacity } - /** - * delete oldest key existing in map by the help of iterator - */ - #removeLeastRecentlyUsed () { - this.cache.delete(this.cache.keys().next().value) - } - set capacity (newCapacity) { if (newCapacity < 0) { throw new RangeError('Capacity should be greater than 0') @@ -53,12 +52,27 @@ class LRUCache { this.#capacity = newCapacity } + /** + * delete oldest key existing in map by the help of iterator + */ + #removeLeastRecentlyUsed () { + this.cache.delete(this.cache.keys().next().value) + } + + /** + * @param {string} key + * @returns {*} + */ has (key) { key = String(key) return this.cache.has(key) } + /** + * @param {string} key + * @param {*} value + */ set (key, value) { key = String(key) // Sets the value for the input key and if the key exists it updates the existing key @@ -69,6 +83,10 @@ class LRUCache { this.cache.set(key, value) } + /** + * @param {string} key + * @returns {*} + */ get (key) { key = String(key) // Returns the value for the input key. Returns null if key is not present in cache @@ -87,6 +105,10 @@ class LRUCache { return null } + /** + * @param {JSON} json + * @returns {LRUCache} + */ parse (json) { const { misses, hits, cache } = JSON.parse(json) @@ -100,6 +122,10 @@ class LRUCache { return this } + /** + * @param {number} indent + * @returns {JSON} - string + */ toString (indent) { const replacer = (_, value) => { if (value instanceof Set) { From 269a3f4e4f8748f066c7ca1864f70758ac26b57a Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Sun, 27 Mar 2022 06:56:30 +0600 Subject: [PATCH 5/5] chore: added default import export --- Cache/LRUCache.js | 2 +- Cache/test/LRUCache.test.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index 71022ff56a..d524bcbaec 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -143,4 +143,4 @@ class LRUCache { } } -export { LRUCache } +export default LRUCache diff --git a/Cache/test/LRUCache.test.js b/Cache/test/LRUCache.test.js index d340dac010..c9acc3dadf 100644 --- a/Cache/test/LRUCache.test.js +++ b/Cache/test/LRUCache.test.js @@ -1,4 +1,4 @@ -import { LRUCache } from '../LRUCache' +import LRUCache from '../LRUCache' import { fibonacciCache } from './cacheTest' describe('Testing LRUCache', () => {