diff --git a/Cache/LRUCache.js b/Cache/LRUCache.js index e416e03776..d524bcbaec 100644 --- a/Cache/LRUCache.js +++ b/Cache/LRUCache.js @@ -1,40 +1,146 @@ class LRUCache { // LRU Cache to store a given capacity of data + #capacity + + /** + * @param {number} capacity - the capacity of LRUCache + * @returns {LRUCache} - sealed + */ constructor (capacity) { - this.cache = new Map() - this.capacity = 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() + + return Object.seal(this) + } + + get info () { + return Object.freeze({ + misses: this.misses, + hits: this.hits, + capacity: this.capacity, + size: this.size + }) + } + + get size () { + return this.cache.size } - 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})` + get capacity () { + return this.#capacity } + 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 + } + + /** + * 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 - 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) } + /** + * @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 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 + } + + /** + * @param {JSON} json + * @returns {LRUCache} + */ + 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 + } + + /** + * @param {number} indent + * @returns {JSON} - string + */ + 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) } } -export { LRUCache } +export default LRUCache diff --git a/Cache/test/LRUCache.test.js b/Cache/test/LRUCache.test.js index b03d609d66..c9acc3dadf 100644 --- a/Cache/test/LRUCache.test.js +++ b/Cache/test/LRUCache.test.js @@ -1,9 +1,19 @@ -import { LRUCache } from '../LRUCache' +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,41 @@ 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 + }) + + 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: 6, + hits: 12, + 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 + }) }) }) pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy