I have a set of users. I need to call "costlyEncryptionFunction" on each user.id, but I don't want to call "costlyEncryptionFunction" multiple times on the same user.id.
Here is a working example:
const costlyEncryptionFunction = async (id) => {
return new Promise((res) => {
setTimeout(() => {
res(id + 1000)
}, 1000)
})
}
let users = [
{id: 1, item: 1},
{id: 1, item: 2},
{id: 2, item: 5},
{id: 2, item: 6}
]
let currentUserId
users.map(async (user) => {
let encryptedUserId
if(!currentUserId || currentUserId != user.id){
currentUserId = user.id
encryptedUserId = await costlyEncryptionFunction(currentUserId)
}
if(encryptedUserId){
console.log(`inside: ${encryptedUserId} ${user.item} ... do more stuff` )
}
})
The output reads
inside: 1001 1 ... do more stuff
inside: 1002 5 ... do more stuff
I am trying to have my output read:
inside: 1001 1 ... do more stuff
inside: 1001 2 ... do more stuff
inside: 1002 5 ... do more stuff
inside: 1002 6 ... do more stuff
Does anyone have a solution to this, other than calling "costlyEncryptionFunction" and repeating the same userId multiple times?
-
Memoize the function?VLAZ– VLAZ2021年03月18日 19:40:41 +00:00Commented Mar 18, 2021 at 19:40
2 Answers 2
The most basic way to do this is to use an object as a cache:
const cache = {};
const costlyEncryptionFunction = async (id) => {
const existing = cache[id];
if (existing) return Promise.resolve(existing);
return new Promise((res) => {
setTimeout(() => {
const encrypted = id + 1000;
cache[id] = encrypted;
res(encrypted);
}, 1000)
})
}
let users = [
{ id: 1, item: 1 },
{ id: 1, item: 2 },
{ id: 2, item: 5 },
{ id: 2, item: 6 }
]
let currentUserId
users.map(async (user) => {
let encryptedUserId
if (!currentUserId || currentUserId != user.id) {
currentUserId = user.id
encryptedUserId = await costlyEncryptionFunction(currentUserId)
}
if (encryptedUserId) {
console.log(`inside: ${encryptedUserId} ${user.item} ... do more stuff`)
}
})
If your target environment supports it, you can use a Map:
const cache = new Map();
const costlyEncryptionFunction = async (id) => {
const existing = cache.has(id) ? cache.get(id) : undefined;
if (existing) return Promise.resolve(existing);
return new Promise((res) => {
setTimeout(() => {
const encrypted = id + 1000;
cache.set(id, encrypted) = encrypted;
res(encrypted);
}, 1000)
})
}
let users = [
{ id: 1, item: 1 },
{ id: 1, item: 2 },
{ id: 2, item: 5 },
{ id: 2, item: 6 }
]
let currentUserId
users.map(async (user) => {
let encryptedUserId
if (!currentUserId || currentUserId != user.id) {
currentUserId = user.id
encryptedUserId = await costlyEncryptionFunction(currentUserId)
}
if (encryptedUserId) {
console.log(`inside: ${encryptedUserId} ${user.item} ... do more stuff`)
}
})
This is more or less what utility libraries do under the hood with their memoize methods, though they typically offer features such as multiple parameters, which complicates the implementation a bit.
1 Comment
Assuming you are using Node.js, You could use the ramda package to memoize the function
const {identity, memoizeWith} = require('ramda');
const costlyEncryptionFunction = async (id) => {
return new Promise((res) => {
setTimeout(() => {
res(id + 1000)
}, 1000)
})
}
const memoizedCostlyFunction = (identity, costlyEncryptionFunction)
let users = [
{id: 1, item: 1},
{id: 1, item: 2},
{id: 2, item: 5},
{id: 2, item: 6}
]
let currentUserId
users.map(async (user) => {
let encryptedUserId
if(true ){
currentUserId = user.id
// encryptedUserId = await costlyEncryptionFunction(currentUserId)
encryptedUserId = await memoizedCostlyFunction(currentUserId)
}
if(encryptedUserId){
console.log(`inside: ${encryptedUserId} ${user.item} ... do more stuff` )
}
})