0

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?

1
  • Memoize the function? Commented Mar 18, 2021 at 19:40

2 Answers 2

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.

Sign up to request clarification or add additional context in comments.

1 Comment

"costlyEncryptionFunction" is obviously not the real encryption function... but yeah that idea will work. I will put a wrapper function around the encryption function.
0

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` )
  }
})

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.