You have acess to remote data using a function named getData
, but you have to pay royalties each time you call it.
You know the data doesn't change in time, but depends on the arguments you're passing to getData
getData('some', 'args') // returns some data
getData('some', 'other') // returns another data
write a function multiMemoize
that tankes a function, here getData
, as an argument and returns an extended version of this function.
The extended version only calls the original function once for given arguments, then returns memoized result if called with the same arguments, like this :
let memoized = multiMemoize(getData)
let res1 = memoized('hello', 'world')
// getData is called
let res2 = memoized('hello', 'world')
// getData is NOT called
res1 === res2 // true
let other1 = memoized('fizz', 'buzz')
// getData is called
let res3 = memoized('hello', 'world')
// getData is NOT called
res3 === res1 // true
// getData was called only 2 times
Constraints
-
The
multiMemoizeFunction
should extend any function whatever the number of arguments -
the extended version should accept any arguments, strings, integer, objects... beware that random tests use random characters
-
it should work properly with promises (you have preloaded function
getDataAsync
to test that):
let asyncMemoized = multiMemoize(getDataAsync)
asyncMemoized(5)
.then(data => console.log(data))
.catch(err => console.log(err))
asyncMemoized(5)
.then(data => console.log(data))
.catch(err => console.log(err))
// gives the same result (immediately)
// WITHOUT calling getDataAsync
helpers
-
you have function
getData
to fetch the data synchonously -
you have function
getDataAsync
to fetch it in a promise that resolves in ~1 second -
you can call
getRequestCount
to follow how many times you called each function :
getRequestCount() === {
sync: 5,
async: 2,
total: 7
}
// or
getRequestCount().sync === 5
- if you want to deal with multiple datasources, you can create another one using the class
DataSource
which exposes methodsgetData
,getDataSync
&getRequestCount
:
let dataSource2 = new DataSource()
dataSource2.getDataAsync()
dataSource2.getDataAsync()
dataSource2.getRequestCount().async === 2
``
function multiMemoize (func) {
let cache = new Map()
function run (...args) {
let key = JSON.stringify(args)
let cached = cache.get(key)
if (cached !== undefined) {
return cached
} else {
let value = func(...args)
cache.set(key, value)
return value
}
}
return run
}
/*
let memoized = multiMemoize(getData)
let res1 = memoized('hello', 'world')
// getData is called
let res2 = memoized('hello', 'world')
// getData is NOT called
res1 === res2 // true
let other1 = memoized('fizz', 'buzz')
// getData is called
let res3 = memoized('hello', 'world')
// getData is NOT called
res3 === res1 // true
console.log(getRequestCount())
*/
const chai = require('chai');
const assert = chai.assert;
chai.config.truncateThreshold = 0;
describe('memoized function', function() {
it('ASYNC : should return the right data with async function', function(done) {
let dataSource = new DataSource()
let getData = (...args)=>{return dataSource.getData(...args)}
let getDataAsync = (...args)=>{return dataSource.getDataAsync(...args)}
let getRequestCount = _ => {return dataSource.getRequestCount()}
let promises = []
let memo = multiMemoize(getDataAsync)
// test 1
promises.push(memo('hello', 'world'))
promises.push(getDataAsync('hello', 'world'))
// test 2 with a number argument
promises.push(memo('test2', 2))
promises.push(getDataAsync('test2', 2))
// test 3 with only one argument
promises.push(memo('test3'))
promises.push(getDataAsync('test3'))
Promise.all(promises)
.then(datas => {
assert.deepEqual(datas[0], datas[1])
assert.deepEqual(datas[2], datas[3])
assert.deepEqual(datas[4], datas[5])
done()
})
.catch(err => done(err))
});
it('ASYNC : should request only once, then get cached value for the same args, and cache multiple values', function(done) {
let dataSource = new DataSource()
let getData = (...args)=>{return dataSource.getData(...args)}
let getDataAsync = (...args)=>{return dataSource.getDataAsync(...args)}
let getRequestCount = _ => {return dataSource.getRequestCount()}
let memo = multiMemoize(getDataAsync)
let promises = []
promises.push(memo('request','identical'))// should request once
promises.push(memo('request','identical'))// should get cache ...
promises.push(memo('request','identical'))
promises.push(memo('request','identical'))
promises.push(memo('request','identical'))
promises.push(memo('request','identical'))
Promise.all(promises)
.then(datas => {
assert.strictEqual(getRequestCount().async, 1)
let promises = []
promises.push(memo('request','different')) // should store a new value
promises.push(memo('request','identical')) // should get the cache again
promises.push(memo('request','different')) // should get the second cache
return Promise.all(promises)
})
.then(datas => {
assert.strictEqual(getRequestCount().async, 2) // only 2 requests have been made
done()
})
.catch(err => done(err))
});
});