From 72def415e7d22c3644075b260af0f678ebe37658 Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Wed, 2 Mar 2022 13:15:10 +0600 Subject: [PATCH 1/5] feat: improved memoize function used Map instead of object & used the JSON.stringfy method for generate a valid string as a key --- Cache/Memoize.js | 38 ++++++++++++++++++++++++-------- Cache/test/Memoize.test.js | 44 +++++++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 10 deletions(-) diff --git a/Cache/Memoize.js b/Cache/Memoize.js index 9bd998fdac..3a8d611678 100644 --- a/Cache/Memoize.js +++ b/Cache/Memoize.js @@ -14,20 +14,37 @@ * @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 is [[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 +52,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 } \ No newline at end of file diff --git a/Cache/test/Memoize.test.js b/Cache/test/Memoize.test.js index 4d156ce347..e92d724cdc 100644 --- a/Cache/test/Memoize.test.js +++ b/Cache/test/Memoize.test.js @@ -16,7 +16,21 @@ const factorial = (n) => { return n * factorial(n - 1) } -describe('Memoize', () => { +const multipleFactorials = (arr) => arr.map(factorial) + +/** + * @title implementation of union function + * @param {Set} sets + * @return {new Set} + */ +function union(...sets) { + return new Set( + sets.reduce((flatArray, set) => [...flatArray, ...set], []) + ) +} + + +describe('Testing Memoize', () => { it('expects the fibonacci function to use the cache on the second call', () => { const memoFibonacci = memoize(fibonacci) @@ -34,4 +48,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)) + }) }) From 8548ef1b3d29ea11b2ad8fd5e1bb10ac27adcb08 Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Wed, 2 Mar 2022 13:17:02 +0600 Subject: [PATCH 2/5] docs: modified documentation --- Cache/Memoize.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/Cache/Memoize.js b/Cache/Memoize.js index 3a8d611678..17444ce480 100644 --- a/Cache/Memoize.js +++ b/Cache/Memoize.js @@ -1,16 +1,14 @@ /** - * 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 */ From ce9d3b9ef4b1dc06600e5893aec67e46ce36b78a Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Wed, 2 Mar 2022 13:18:54 +0600 Subject: [PATCH 3/5] style: format with standard --- Cache/Memoize.js | 2 +- Cache/test/Memoize.test.js | 7 +++---- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Cache/Memoize.js b/Cache/Memoize.js index 17444ce480..a555c41f97 100644 --- a/Cache/Memoize.js +++ b/Cache/Memoize.js @@ -57,4 +57,4 @@ const memoize = (func) => { } } -export { memoize } \ No newline at end of file +export { memoize } diff --git a/Cache/test/Memoize.test.js b/Cache/test/Memoize.test.js index e92d724cdc..7f38db3a79 100644 --- a/Cache/test/Memoize.test.js +++ b/Cache/test/Memoize.test.js @@ -20,16 +20,15 @@ const multipleFactorials = (arr) => arr.map(factorial) /** * @title implementation of union function - * @param {Set} sets + * @param {Set} sets * @return {new Set} */ -function union(...sets) { +function union (...sets) { return new Set( sets.reduce((flatArray, set) => [...flatArray, ...set], []) ) } - describe('Testing Memoize', () => { it('expects the fibonacci function to use the cache on the second call', () => { const memoFibonacci = memoize(fibonacci) @@ -52,7 +51,7 @@ describe('Testing Memoize', () => { 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)) }) From 1e2fb53e7613aab4eee4829db3b98b9298c363ab Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Wed, 2 Mar 2022 13:21:29 +0600 Subject: [PATCH 4/5] docs: modified stringify doc --- Cache/Memoize.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cache/Memoize.js b/Cache/Memoize.js index a555c41f97..c69ae48011 100644 --- a/Cache/Memoize.js +++ b/Cache/Memoize.js @@ -31,7 +31,7 @@ const memoize = (func) => { return (...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 is [[1, 2, 3, 4], {name: 'myName', age: 23}] + * 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 */ From 2a5c28bb1fb4c995287ab28da22ae7fb142d1fd4 Mon Sep 17 00:00:00 2001 From: Fahim Faisaal Date: Wed, 2 Mar 2022 14:10:41 +0600 Subject: [PATCH 5/5] refactor: remove two repetition implementation --- Cache/test/Memoize.test.js | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/Cache/test/Memoize.test.js b/Cache/test/Memoize.test.js index 7f38db3a79..f6ecee7129 100644 --- a/Cache/test/Memoize.test.js +++ b/Cache/test/Memoize.test.js @@ -1,20 +1,6 @@ import { memoize } from '../Memoize' - -const fibonacci = (n) => { - if (n < 2) { - return n - } - - return fibonacci(n - 2) + fibonacci(n - 1) -} - -const factorial = (n) => { - if (n === 0) { - return 1 - } - - return n * factorial(n - 1) -} +import { fibonacci } from '../../Dynamic-Programming/FibonacciNumber' +import { factorial } from '../../Recursive/Factorial' const multipleFactorials = (arr) => arr.map(factorial)