Skip to content

Instantly share code, notes, and snippets.

@mhawksey
Created February 7, 2024 21:55
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mhawksey/37b1399f064de3559bdc8e35b1944a42 to your computer and use it in GitHub Desktop.
Save mhawksey/37b1399f064de3559bdc8e35b1944a42 to your computer and use it in GitHub Desktop.
Examples of Memoization in Apps Script with Cache Service
function main(){
console.log("Run in memory tests")
inMemory();
console.log("Run in cache tests")
withCache();
}
// From https://medium.com/@saravanaeswari22/memorization-in-javascript-f312d66402b2
const memoize = (func) => {
const results = {};
return (...args) => {
const argsKey = JSON.stringify(args);
if (!results[argsKey]) {
results[argsKey] = func(...args);
}
return results[argsKey];
};
};
const clumsysquare = memoize((num) => {
let result = 0;
for (let i = 1; i <= num; i++) {
for (let j = 1; j <= num; j++) {
result++;
}
}
return result;
});
// From https://justin.poehnelt.com/posts/apps-script-memoization/
/**
* A generic hash function that takes a string and computes a hash using the
* specified algorithm.
*
* @param {string} str - The string to hash.
* @param {Utilities.DigestAlgorithm} algorithm - The algorithm to use to
* compute the hash. Defaults to MD5.
* @returns {string} The base64 encoded hash of the string.
*/
function hash(str, algorithm = Utilities.DigestAlgorithm.MD5) {
const digest = Utilities.computeDigest(algorithm, str);
return Utilities.base64Encode(digest);
}
/**
* Memoizes a function by caching its results based on the arguments passed.
*
* @param {Function} func - The function to be memoized.
* @param {number} [ttl=600] - The time to live in seconds for the cached
* result. The maximum value is 600.
* @param {Cache} [cache=CacheService.getScriptCache()] - The cache to store the
* memoized results.
* @returns {Function} - The memoized function.
*
* @example
*
* const cached = memoize(myFunction);
* cached(1, 2, 3); // The result will be cached
* cached(1, 2, 3); // The cached result will be returned
* cached(4, 5, 6); // A new result will be calculated and cached
*/
function memoizeWithCache(func, ttl = 600, cache = CacheService.getScriptCache()) {
return (...args) => {
// consider a more robust input to the hash function to handler complex
// types such as functions, dates, and regex
const key = hash(JSON.stringify([func.toString(), ...args]));
//const key = JSON.stringify([func.toString(), ...args]);
const cached = cache.get(key);
if (cached != null) {
return JSON.parse(cached);
} else {
const result = func(...args);
cache.put(key, JSON.stringify(result), ttl);
return result;
}
};
}
const clumsysquareWithCache = memoizeWithCache((num) => {
let result = 0;
for (let i = 1; i <= num; i++) {
for (let j = 1; j <= num; j++) {
result++;
}
}
return result;
}, 3600, CacheService.getUserCache());
// Tests to compare different speeds
function inMemory() {
console.time("First call");
console.log(clumsysquare(9467));
console.timeEnd("First call");
// use the same value two times
console.time("Second call");
console.log(clumsysquare(9467));
console.timeEnd("Second call");
console.time("Third call");
console.log(clumsysquare(9467));
console.timeEnd("Third call");
}
function withCache() {
console.time("First call");
console.log(clumsysquareWithCache(9467));
console.timeEnd("First call");
// use the same value two times
console.time("Second call");
console.log(clumsysquareWithCache(9467));
console.timeEnd("Second call");
console.time("Third call");
console.log(clumsysquareWithCache(9467));
console.timeEnd("Third call");
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment