diff --git a/Cache/Memoize.js b/Cache/Memoize.js index 9bd998fdac..c69ae48011 100644 --- a/Cache/Memoize.js +++ b/Cache/Memoize.js @@ -1,33 +1,48 @@ /** - * Memoize - * + * @function memoize + * @description -> * From [Wikipedia](https://en.wikipedia.org/wiki/Memoization), * memoization is an optimization technique * used primarily to speed up computer programs, * by storing the results of expensive function calls * and returning the cached result when the same inputs occur again - * * This function is a first class objects, * which lets us use it as [Higher-Order Function](https://eloquentjavascript.net/05_higher_order.html) * and return another function - * * @param {Function} func Original function * @returns {Function} Memoized function */ -export const memoize = (func) => { - // Initialization of a slot to store the function result - const cache = {} +const memoize = (func) => { + // Initialization of a slot to store the function result by arguments as a key in Hash Map + const cache = new Map() + + const jsonReplacer = (_, value) => { + if (value instanceof Set) { // if the value is Set it's converted to Array cause JSON.stringify can't convert Set + return [...value] + } + + if (value instanceof Map) { // if the value is Map it's converted to Object cause JSON.stringify can't convert Map + return Object.fromEntries(value) + } + + return value + } return (...args) => { - // Retrieving the first argument of the function - const [arg] = args + /** + * Arguments converted to JSON string for use as a key of Map - it's easy to detect collections like -> Object and Array + * If the args input is -> [new Set([1, 2, 3, 4]), {name: 'myName', age: 23}] + * Then the agrsKey generate to -> '[[1,2,3,4],{"name":"myName","age":23}]' which is JSON mean string + * Now it's ready to be a perfect key for Map + */ + const argsKey = JSON.stringify(args, jsonReplacer) /** * Checks if the argument is already present in the cache, * then return the associated value / result */ - if (arg in cache) { - return cache[arg] + if (cache.has(argsKey)) { + return cache.get(argsKey) } /** @@ -35,8 +50,11 @@ export const memoize = (func) => { * execute original function and save its value / result in cache, * finally return it */ - const result = func(arg) - cache[arg] = result + const result = func(...args) // spread all args + cache.set(argsKey, result) + return result } } + +export { memoize } diff --git a/Cache/test/Memoize.test.js b/Cache/test/Memoize.test.js index 4d156ce347..f6ecee7129 100644 --- a/Cache/test/Memoize.test.js +++ b/Cache/test/Memoize.test.js @@ -1,22 +1,21 @@ import { memoize } from '../Memoize' +import { fibonacci } from '../../Dynamic-Programming/FibonacciNumber' +import { factorial } from '../../Recursive/Factorial' -const fibonacci = (n) => { - if (n < 2) { - return n - } +const multipleFactorials = (arr) => arr.map(factorial) - return fibonacci(n - 2) + fibonacci(n - 1) +/** + * @title implementation of union function + * @param {Set} sets + * @return {new Set} + */ +function union (...sets) { + return new Set( + sets.reduce((flatArray, set) => [...flatArray, ...set], []) + ) } -const factorial = (n) => { - if (n === 0) { - return 1 - } - - return n * factorial(n - 1) -} - -describe('Memoize', () => { +describe('Testing Memoize', () => { it('expects the fibonacci function to use the cache on the second call', () => { const memoFibonacci = memoize(fibonacci) @@ -34,4 +33,32 @@ describe('Memoize', () => { expect(memoFactorial(10)).toEqual(factorial(10)) expect(memoFactorial(10)).toEqual(3628800) }) + + it('expects the multipleFactorials function to use the cache on the second call', () => { + const memoMultipleFactorials = memoize(multipleFactorials) + const input = [2, 3, 4, 5] + + expect(memoMultipleFactorials(input)).toEqual([2, 6, 24, 120]) + expect(memoMultipleFactorials(input)).toEqual(multipleFactorials(input)) + }) + + it('expects the multipleFactorials function to use the cache on the second call', () => { + const memoMultipleFactorials = memoize(multipleFactorials) + const input = [2, 3, 4, 5] + + expect(memoMultipleFactorials(input)).toEqual([2, 6, 24, 120]) + expect(memoMultipleFactorials(input)).toEqual(multipleFactorials(input)) + }) + + it('expects the union function to use the cache on the second call', () => { + const memoUnion = memoize(union) + const inputs = [ + new Set([1, 2, 3]), + new Set([4, 3, 2]), + new Set([5, 3, 6]) + ] + + expect(memoUnion(...inputs)).toEqual(new Set([1, 2, 3, 4, 5, 6])) + expect(memoUnion(...inputs)).toEqual(union(...inputs)) + }) })