I have the following code and I am trying to prevent the calculation inside of complexFunction in case same arguments are passed.
const complexFunction = (arg1, arg2) => {
/* complex calculation in here */
console.log('complexFunction');
return true;
};
// this is the method we need to implement
const memo=()=>{
}
const memoComplex = memo(complexFunction);
memoComplex(1, 2); // complex function should be executed
memoComplex(1, 2); // complex function should not be invoked again, instead the cached result should be returned
memoComplex(1, 3); // complex function should be executed
CodePudding user response:
You can simply use an array of objects which holds the arguments to check if they've been used before, the code below demonstrates the use
const usedArgs = [];
const memoComplex = (arg1, arg2) => {
/* complex calculation in here */
const value = arg1 arg2;
usedArgs.push({ arg1: arg1, arg2: arg2, data: value });
return value;
};
const memo = (arg1, arg2) => {
const exists = usedArgs.find(x => x.arg1 == arg1 && x.arg2 == arg2);
if (exists) {
console.log('loading from cache')
return exists.data;
} else return memoComplex(arg1, arg2);
}
console.log(memo(1, 2)); // complex function should be executed
console.log(memo(1, 2)); // complex function should not be invoked again, instead the cached result should be returned
console.log(memo(1, 3)); // complex function should be executed
CodePudding user response:
What you're asking for is called memoization -- essentially caching results of a function call (in this case) and reusing these when the same call is made later in time.
I haven't found myself using it, but it's obviously a thing -- having its own name -- and I find your question interesting so I decided to give it a go.
Conceptually, you'd check upon every invocation if the arguments to the function constitute a known set and if they do return a result that is related to that set. This result would have been stored by a previous call to the function, for the set of arguments it was called with.
In practice and with JavaScript, however, there are two caveats:
If some of your arguments are objects and these objects are mutable, you have to decide how the identity of the object relates to its mutability -- should, for instance, changing the property of the object, change the computation result of the function that is passed the object?
If you pass the same set of arguments to a function, this set is usually available as a unique object -- even if the same arguments were passed to the function before. This is true for whether the function was declared as
function complexFunction(...args)
(args
is all of the arguments passed) or ascomplexFunction()
(relying on the "automatic"arguments
variable). There is no way to retain the syntax of passing arguments separated by commas and have the script host magically provide you with the same array for the same set of arguments.
There is a solution if you are willing to trade in some "readability" and pass your arguments as one single object instead of passing them separated by commas.
Below is a function that when passed a function (e.g. your complexFunction
), returns a function which when called will either return previously computed result for some set of arguments passed earlier, or call the latter storing the result for subsequent calls:
function memoize(fn, results) {
return function(args) {
if(results.has(args)) return results.get(args);
else {
const result = fn(...args);
results.set(args, result);
return result;
}
}
}
You only need to create a "memoized" variant of your complexFunction
once:
const memoized_complexFunction = memoize(complexFunction, new WeakMap());
You may then use memoized_complexFunction
almost the same way you'd use complexFunction
(note how the arguments are all passed as one), with the benefits of caching:
const args = [ 777, -55 ];
memoized_complexFunction(args);
Every time you call memoized_complexFunction
, it will first check if there is a previously computed result for the array of arguments being passed and if there is, it will return that result, foregoing expensive computation. If there is no such result, it will call the function for you as you would regularly, then store its result in the results map and return that result. Subsequent calls for the same set of arguments (meaning you pass the same array object as before, not another array of values identical to those in the first) will eliminate repeated computation.
CodePudding user response:
You could save the arguments passed and the result in Session Storage and on method call, look if you have already saved that in the storage, if that the case you could get it and return the result. Otherwise you would execute your method as always
sessionStorage.setItem('clé', 'valeur');
const complexFunction = (arg1, arg2) => {
// Get item return null if not found
let potentialResult = sessionStorage.getItem("#1" arg1.toString() "#2" arg2.toString());
if(potentialResult !== null) {
// If we have a result in session storage, that mean we have already done the calc
// so we return the result
return potentialResult;
} else {
// If we don't have a result, we just execute the function and save the result
/* complex calculation in here */
let result = "Your result"; // Assuming result is the result of your function
// We save the result to avoid future calculation
sessionStorage.setItem("#1" arg1.toString() "#2" arg2.toString(), result);
console.log('complexFunction');
return true;
}
};
But as said in the comments, this is more a hint than a solution, so please adapt it to your use case