Skip to content

Commit 6656ece

Browse files
authored
merge: Improved Memoize function (#912)
* feat: improved memoize function used Map instead of object & used the JSON.stringfy method for generate a valid string as a key * docs: modified documentation * style: format with standard * docs: modified stringify doc * refactor: remove two repetition implementation
1 parent eb748ae commit 6656ece

File tree

2 files changed

+72
-27
lines changed

2 files changed

+72
-27
lines changed

Cache/Memoize.js

Lines changed: 31 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,42 +1,60 @@
11
/**
2-
* Memoize
3-
*
2+
* @function memoize
3+
* @description ->
44
* From [Wikipedia](https://en.wikipedia.org/wiki/Memoization),
55
* memoization is an optimization technique
66
* used primarily to speed up computer programs,
77
* by storing the results of expensive function calls
88
* and returning the cached result when the same inputs occur again
9-
*
109
* This function is a first class objects,
1110
* which lets us use it as [Higher-Order Function](https://eloquentjavascript.net/05_higher_order.html)
1211
* and return another function
13-
*
1412
* @param {Function} func Original function
1513
* @returns {Function} Memoized function
1614
*/
17-
export const memoize = (func) => {
18-
// Initialization of a slot to store the function result
19-
const cache = {}
15+
const memoize = (func) => {
16+
// Initialization of a slot to store the function result by arguments as a key in Hash Map
17+
const cache = new Map()
18+
19+
const jsonReplacer = (_, value) => {
20+
if (value instanceof Set) { // if the value is Set it's converted to Array cause JSON.stringify can't convert Set
21+
return [...value]
22+
}
23+
24+
if (value instanceof Map) { // if the value is Map it's converted to Object cause JSON.stringify can't convert Map
25+
return Object.fromEntries(value)
26+
}
27+
28+
return value
29+
}
2030

2131
return (...args) => {
22-
// Retrieving the first argument of the function
23-
const [arg] = args
32+
/**
33+
* Arguments converted to JSON string for use as a key of Map - it's easy to detect collections like -> Object and Array
34+
* If the args input is -> [new Set([1, 2, 3, 4]), {name: 'myName', age: 23}]
35+
* Then the agrsKey generate to -> '[[1,2,3,4],{"name":"myName","age":23}]' which is JSON mean string
36+
* Now it's ready to be a perfect key for Map
37+
*/
38+
const argsKey = JSON.stringify(args, jsonReplacer)
2439

2540
/**
2641
* Checks if the argument is already present in the cache,
2742
* then return the associated value / result
2843
*/
29-
if (arg in cache) {
30-
return cache[arg]
44+
if (cache.has(argsKey)) {
45+
return cache.get(argsKey)
3146
}
3247

3348
/**
3449
* If the argument is not yet present in the cache,
3550
* execute original function and save its value / result in cache,
3651
* finally return it
3752
*/
38-
const result = func(arg)
39-
cache[arg] = result
53+
const result = func(...args) // spread all args
54+
cache.set(argsKey, result)
55+
4056
return result
4157
}
4258
}
59+
60+
export { memoize }

Cache/test/Memoize.test.js

Lines changed: 41 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
import { memoize } from '../Memoize'
2+
import { fibonacci } from '../../Dynamic-Programming/FibonacciNumber'
3+
import { factorial } from '../../Recursive/Factorial'
24

3-
const fibonacci = (n) => {
4-
if (n < 2) {
5-
return n
6-
}
5+
const multipleFactorials = (arr) => arr.map(factorial)
76

8-
return fibonacci(n - 2) + fibonacci(n - 1)
7+
/**
8+
* @title implementation of union function
9+
* @param {Set} sets
10+
* @return {new Set}
11+
*/
12+
function union (...sets) {
13+
return new Set(
14+
sets.reduce((flatArray, set) => [...flatArray, ...set], [])
15+
)
916
}
1017

11-
const factorial = (n) => {
12-
if (n === 0) {
13-
return 1
14-
}
15-
16-
return n * factorial(n - 1)
17-
}
18-
19-
describe('Memoize', () => {
18+
describe('Testing Memoize', () => {
2019
it('expects the fibonacci function to use the cache on the second call', () => {
2120
const memoFibonacci = memoize(fibonacci)
2221

@@ -34,4 +33,32 @@ describe('Memoize', () => {
3433
expect(memoFactorial(10)).toEqual(factorial(10))
3534
expect(memoFactorial(10)).toEqual(3628800)
3635
})
36+
37+
it('expects the multipleFactorials function to use the cache on the second call', () => {
38+
const memoMultipleFactorials = memoize(multipleFactorials)
39+
const input = [2, 3, 4, 5]
40+
41+
expect(memoMultipleFactorials(input)).toEqual([2, 6, 24, 120])
42+
expect(memoMultipleFactorials(input)).toEqual(multipleFactorials(input))
43+
})
44+
45+
it('expects the multipleFactorials function to use the cache on the second call', () => {
46+
const memoMultipleFactorials = memoize(multipleFactorials)
47+
const input = [2, 3, 4, 5]
48+
49+
expect(memoMultipleFactorials(input)).toEqual([2, 6, 24, 120])
50+
expect(memoMultipleFactorials(input)).toEqual(multipleFactorials(input))
51+
})
52+
53+
it('expects the union function to use the cache on the second call', () => {
54+
const memoUnion = memoize(union)
55+
const inputs = [
56+
new Set([1, 2, 3]),
57+
new Set([4, 3, 2]),
58+
new Set([5, 3, 6])
59+
]
60+
61+
expect(memoUnion(...inputs)).toEqual(new Set([1, 2, 3, 4, 5, 6]))
62+
expect(memoUnion(...inputs)).toEqual(union(...inputs))
63+
})
3764
})

0 commit comments

Comments
 (0)