0

I have large array of objects and filtered the objects based on the userID. Here is the code below.

const filteredArr = LargeArr.Items.reduce(
            async(acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
                let pic = null;
                if (picture) { pic = await getPic(picture); } // async here
                acc[userID] = { name, userID, pic, dob };
                return acc;
            }, {});

Expected Output :

{
  '1595232114269': {
    name: 'Mark Status',
    userID: '1595232114269',
    picture: 'mark-status.jpg',
    dob: '2020-08-10'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d': {
    name: 'Jack Thomas',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d23d',
    picture: 'jack-thomas.jpg',
    dob: '1990-12-20'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p': {
    name: 'Petro Huge',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d47p',
    picture: 'petro huge.jpg',
    dob: '1856-12-20'
  },
  '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j': {
    name: 'Mark Henry',
    userID: '48e69555d778f9b9a3a1d553b9c3b8f7dd6a3394ac82df1433b60a69c055d55j',
    picture: 'mark-henry.jpg',
    dob: '2005-12-29'
  }
}

I need to get picture from an api which is asynchronous, so used async await inside the reduce method. The problem here is it is always showing as Promise pending. If this was an array of object, then i can return Promise.all, but since this is object containing object how can i proceed with this inside reduce method? I need the exact same expected output.

Can somebody help me with this? Any help would be really appreciated.

7
  • 1
    acc will be a promise after the first iteration (since async functions return a promise). Commented Sep 3, 2020 at 7:20
  • Why is the name of the variable filteredArr when its content is an object? The name suggests that it stores the result of a Array.prototype.filter() call. Commented Sep 3, 2020 at 7:21
  • @FelixKling yes can you let me know how can we do this? Commented Sep 3, 2020 at 7:21
  • You could use Promise.all() to make all the necessary API calls first, then use .reduce after and pass a sync function. Commented Sep 3, 2020 at 7:21
  • 1
    Just don't use reduce here :) It makes things unnecessarily complex. Commented Sep 3, 2020 at 7:23

1 Answer 1

1

To use reduce while iterating over items asynchronously, you'd have to have the accumulator which gets passed from callback to callback to be a Promise. While this is possible, it'll make things pretty difficult to read, and introduces some unnecessary syntax noise.

Use a plain for loop instead:

const filteredArr = {};
for (const item of LargeArr.Items) {
  const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
  const pic = picture ? await getPic(picture) : null;
  filteredArr[userID] = { name, uesrID, pic, dob };
}

If you really wanted to take the reduce route:

LargeArr.Items.reduce(
  (acc, { attributes: { dob, name, picture} = { dob: null, name: null, picture: null }, userID }) => {
    return acc.then(async (acc) => {
      let pic = null;
      if (picture) { pic = await getPic(picture); } // async here
      acc[userID] = { name, userID, pic, dob };
      return acc;
    });
  }, Promise.resolve({})
)
  .then((filteredArr) => {
    // do stuff with filteredArr
  });

Unless the getPic calls need to be made in serial, you could consider using Promise.all instead, to iterate through the whole array at once, rather than waiting on the resolution of the prior Promise before going onto the next.

If your API can handle Promise.all:

const filteredArr = {};
await Promise.all(LargeArr.Items.map(async (item) => {
  const { attributes: { dob, name, picture} = { dob: null, name: null, picture: null } } = item;
  const pic = picture ? await getPic(picture) : null;
  filteredArr[userID] = { name, uesrID, pic, dob };
}));
Sign up to request clarification or add additional context in comments.

8 Comments

The second example won't work since acc isn't a promise in the first iteration. But if you do acc = await acc; instead of return acc.then(...) (and make the outer function async) then it should work.
@certainperformance inside for of const attributes destructuring, i think one brace is missing right?
@Vishnu Yeah, fixed it. Nested destructuring makes things hard to read and write, might want to avoid it when possible
@CertainPerformance I didnt get your point on getPic calls made serial and using Promise.all. It would be great if you can explain a bit. Thanks.
@Vishnu Say getPic takes 1 second. Then, if there are 5 pictures in the array, your current method and for..of will take 5 seconds total. But if you use Promise.all, you can send all requests out at once, so it'll only take 1 second total. But if getPic needs to run in serial (one after the other), then stick with the for loop instead of Promise.all.
|

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.