0

I have an issue, or at least I'm not having the solution.

I'm getting some API calls via a map and I can console.log all results which is great, but the problem is that I want to combine all the results in one array.

var alpha = ['a', 'b', 'c', 'd', 'e'];

alpha.map(alpha => {
 fetch(`https://myurl.com/api/members/page?prefix=${alpha}`)
 .then(res => res.json())
 .then(data => matches.push)
 .then(() => console.log(matches))
})

I guess I have to do some sort of await or something?

My response from the API looks like this:

{"list_complete":true,"keys":
    [
        {
            "username": "name.com",
            "uuid": "-47c9-88b6-2474090c7927",
            "user_type": 1,
            "subscribed": "false",
            "lastLogin": 1611066809086,
            "profile": {
                "name": "Name Lastname",
                "email": "name.com",
                "uuid": "3e92f458-6331-2-88b6-2474090c7927",
                "address": "",
                "history": [
                    {
                        "titleId": "5fac58f764e6710017411e79",
                        "posterUrl": "url.jpg",
                        "displayName": "Guns N ´ Roses - Appetite for Democracy",
                        "t": 0,
                        "d": 8492.2
                    },
                    {
                        "titleId": "5f7eadb3963c170017a919f3",
                        "posterUrl": "url.jpg",
                        "displayName": "Frank Zappa - Apostrophe Overnite Sensation (Classic Albums)",
                        "t": 7.728575,
                        "d": 2974.9
                    },
                    {
                        "titleId": "5e4463a395c832405e7effc0",
                        "posterUrl": "url.jpg",
                        "displayName": "Bob Marley - Uprising Live!",
                        "t": 3285.406821,
                        "d": 6807.7
                    },
                    {
                        "titleId": "5f80c6d0045fdf0017735019",
                        "posterUrl": "url.jpg",
                        "displayName": "Van Morrison - In Concert",
                        "t": 3610.529879,
                        "d": 4558.29
                    },
                    {
                        "titleId": "5fa85aba9c4a1900177e5cf9",
                        "posterUrl": "url.jpg",
                        "displayName": "Iron Maiden - En Vivo!",
                        "t": 2522.988949,
                        "d": 3380.5
                    },
                    {
                        "titleId": "5f719cb75d994e0017b429c5",
                        "posterUrl": "url.jpg",
                        "displayName": "Placebo - Placebo Live At The O2 Brixton Academy",
                        "t": 1426.589863,
                        "d": 5061.89
                    },
                    {
                        "titleId": "5fb3fd1731be640017c2f878",
                        "posterUrl": "https://slam-assets.s3.eu-north-1.amazonaws.com/staging/5fb3fd1731be640017c2f878/cover/1606214166013_nirvanastaende.jpg",
                        "displayName": "Nirvana - Nevermind (Classic Albums)",
                        "t": 0,
                        "d": 2948.69
                    }
                ],
                "favourites": [
                    "5f9039ed1279d600170378c2",
                    "5facf94364e6710017411e7d",
                    "5e4463a395c832405e7effc0"
                ]
            },
            "subscription": null
        }
    ]
}

And the data I want to collect is in a array called history under each user.

  • 1
    Look into [`Promise.all`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/all) and related methods. How exactly do you plan to use the result values? – Sebastian Simon Mar 08 '21 at 22:22
  • Cool. thank you. Will have a look. When I have all the data collected I will combine all users history into one. – Mikael Blytung Mar 08 '21 at 22:23
  • 2
    If `matches` is an array, `matches.push` ain't gonna do much. You'd want `.then(matches.push)` if you want to push `data` into `matches` (or `then(data => { matches.push(data); })` so that you don't get a response of the new length of `matches` (the output of `push`). – Heretic Monkey Mar 08 '21 at 22:27
  • See here for an example of using `Promise.all()`: https://flaviocopes.com/how-to-wait-multiple-promises-javascript/ – Carsten Massmann Mar 08 '21 at 22:28
  • 1
    @HereticMonkey I know, you can’t edit the comment anymore, but just for the record, `.then(matches.push)` won’t work due to the [method context being lost](https://stackoverflow.com/q/30486345/4642212); it’d need to be `.then(matches.push.bind(matches))`. – Sebastian Simon Mar 08 '21 at 22:42

4 Answers4

1

To my understanding you are getting data as a result of your fetch, and you will get as many returns of data for the elements of the alpha array. You would like to combine the results of your history object, from the data, into a single one-dimensional array. That means that your target array looks like:

histories = [{},{},{} ... ]

Where each object {} is a history element.

In order to perform the required operation we need to access the keys elements first and iterate through all of those, retrieving the history contents from each of them. However, you mentioned history may or may not exist for a user, thus we need to check for its existance .

So, summing up:

  • We need to iterate through all the element of keys

  • We need to check if within the above elements, history exists before trying to do something with it.

We may perform the first action by using a forEach() method on data.keys, whilst a simple if (element.profile.history) to check for the presence of history:

var alpha = ['a', 'b', 'c', 'd', 'e'];
const histories = [];

alpha.map(alpha => {
    fetch(`https://myurl.com/api/members/page?prefix=${alpha}`)
        .then(res => res.json())
        .then(data => data.keys.forEach(function (e) { //Iterate through the keys
            if (e.profile.history) { //If history is present
                histories.push(...e.profile.history); //Pushing to array with spread sytnax
            }
        })
        )
})

In order to have one single array with all the history items, we made use of the spread ... syntax, you can read more about it here

So the output of:

console.log(histories)

Will then yield:

[
  {
    titleId: '5fac58f764e6710017411e79',
    posterUrl: 'url.jpg',
    displayName: 'Guns N ´ Roses - Appetite for Democracy',
    t: 0,
    d: 8492.2
  },
  {
    titleId: '5f7eadb3963c170017a919f3',
    posterUrl: 'url.jpg',
    displayName: 'Frank Zappa - Apostrophe Overnite Sensation (Classic Albums)',
    t: 7.728575,
    d: 2974.9
  },
  {
    titleId: '5e4463a395c832405e7effc0',
    posterUrl: 'url.jpg',
    displayName: 'Bob Marley - Uprising Live!',
    t: 3285.406821,
    d: 6807.7
  },
...
]
BiOS
  • 2,282
  • 3
  • 10
  • 24
  • Such a big help. Lots of thanks again. Of course I up voted it and marked it as solved. Ive learned a lot and tested a lot. So im grateful. Yes, was what I was thinking also but cant access it outside when im trying to loop it it out. – Mikael Blytung Mar 09 '21 at 18:41
  • No problem at all and thanks very much! Regarding this other problem: `histories` is actually accessible from outside the `map()` function, you can try yourself by performing something like `console.log(histories[0].titleId)` outside of the map()` as this will print the titleId of the **first** history. Is this more or less what you are looking to do? – BiOS Mar 09 '21 at 18:47
0

Have you tried pushing them into an array?

var alpha = ['a', 'b', 'c', 'd', 'e'];
const results = [];

alpha.map(alpha => {
fetch(`https://myurl.com/api/members/page?prefix=${alpha}`)
  .then(res => res.json())
  .then(data => results.push(data))
})
Cromix
  • 48
  • 3
0

Where you are using alpha.map, I would suggest alpha.forEach, as you are only looping there, not mapping.

You could use await instead of .then, but that's just the way you handle the Promise returned from await.


const urls = [/* your urls here */];
const matches = [];
urls.forEach( url => {
  const res = await fetch(url);
  const json = await res.json();
  matches.push(json);
});
console.log(matches);

Using your existing style, push the data into the matches array, and log once you're done:

var alpha = ['a', 'b', 'c', 'd', 'e'];
var matches = [];
alpha.forEach(alpha => {
 fetch(`https://myurl.com/api/members/page?prefix=${alpha}`)
 .then(res => res.json())
 .then(data => matches.push(data))
});
console.log(matches);

You could use Promise.all to run the fetch calls in parallel, but that's another story.

I think perhaps your JSON returned in an array, so you end up with an array of arrays? Take a look at Array.concat, and use it to add the res.json to the matches array.

Lee Goddard
  • 10,680
  • 4
  • 46
  • 63
0

You could solve that using Promise.all, like this:

var alpha = ['a', 'b', 'c', 'd', 'e'];

var memberHistories = await Promise.all([...alpha.map(item => {
  return fetch(`https://myurl.com/api/members/page?prefix=${item}`)
    .then(res => res.json())
    .then(data => data.keys.filter((e) => e.profile.history).flatMap((e) => e.profile.history));
})]);

var allHistories = memberHistories.flat();

Note the use of flatMap() for merging multiple keys' histories then finally flat() for merging the history entries for all members.

Jonas Äppelgran
  • 2,617
  • 26
  • 30