1

I have a scenario where I need to call a "getData" method. This "getData" method will call the server to get items, then for each item i need to get the child items. The "getData" method should return a single array of ALL of the child items in a single array.

For example, i have

  • item1
    • childA, childB, childC
  • item2
    • childD
  • item3
    • childE, childF

I would like to get a single array containing

[childA, childB, childC, childD, childE, childF]

I have tried the following code but this is not quite right.

export function getData() {
    
  return getItems()
    .then((items) =>{
      var promises = [];
      
      items.forEach((item) => {
        
        promises.push(          
            return getChildItems({
                item: `${item}`,
            })
            .then((childItems) => {
              return childItems
            }),       
        );                              
      });   

      return Promise.all(promises)
        .then((allChildItems) => allChildItems);
    });
}

This returns an array where each element is an array. The top level array's number of elements is the number of items. Each child array contains the number of elements matching the number of child items for that item. For example,

[ 
  [childA, childB, childC], 
  [childD], 
  [childE, childF]
]

How can i get it to return a single array like

[childA, childB, childC, childD, childE, childF]

UPDATE:

I found a solution, but I do not this its particularly elegant. In the PromiseAll, i loop over the top level items and them concat the child arrays into a single array and return it,

return Promise.all(promises)
.then((arrayOfChildItemsArrays) => {
  let allChildItems = []
  arrayOfChildItemsArrays.map((childItemsArray) => {
    allChildItems = allChildItems.concat(childItemsArray);
  });
  return allChildItems;
});

Surely there is a nicer way to do this?

se22as
  • 2,282
  • 5
  • 32
  • 54

2 Answers2

1

You can flatten the array via Array.prototype.flat:

return Promise.all(promises).then(allChildItems => allChildItems.flat());
Aplet123
  • 33,825
  • 1
  • 29
  • 55
0

One solution is to keep your current code and call flat(Infinity) on the result. That'll give you a flattened array. Here's a slightly shortened version of that:

export function getData() {
  return getItems()
    .then((items) => Promise.all(items.map(item => getChildItems({item: `${item}`))))
    .then((childArrays) => {
      return childArrays.flat(Infinity);
    });
}

I've used Infinity there, but the default is 1 and that would probably be good enough for your use case.

Alternatively, you can loop through them yourself (flat is relatively new, but it's also easily polyfilled):

export function getData() {
  return getItems()
    .then((items) => Promise.all(items.map(item => getChildItems({item: `${item}`))))
    .then((childArrays) => {
      const result = [];
      for (const array of childArrays) {
          result.push(...array);
      }
      return result;
    });
}
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Thank you for your comment. After I posted my question I realised I could flatten the array manually myself (hence the update to the post). But i didnt know about .flat, thank you – se22as Jul 24 '20 at 13:42