Use axios request insde a for loop - javascript

I'm beginner in js. I'm using vuejs, axios and this api https://www.balldontlie.io to work on a homework project. This project must be to consult nba players stats. I need your help to solve problems. Here is my code :
var app = new Vue({
el: '#app',
data: {
teams: null,
players: [],
selectedTeam: null,
selectedTeamPlayers: [],
selection: false,
id : [],
season_averages : []
},
methods: {
getAllPlayers() {
axios.get('https://www.balldontlie.io/api/v1/players?per_page=100').then(response => {
let total = response.data.meta.total_pages;
let req = [];
let url = 'https://www.balldontlie.io/api/v1/players?per_page=100&page=';
for (let i = 1; i <= total; i++) {
req.push(axios.get(url + i));
}
axios.all(req).then(axios.spread((...responses) => {
for (let i = 0; i < responses.length; i++) {
let jsonPlayers = responses[i].data.data;
for (let j = 0; j < jsonPlayers.length; j++) {
this.players.push(jsonPlayers[j]);
this.id.push(jsonPlayers[j].id);
}
}
console.log(this.id);
}));
});
},
getSeasons() {
let seasons = getAllplayers();
let sa = [];
for (var i = 0; i < seasons; i++) {
axios.get("https://www.balldontlie.io/api/v1/season_averages?player_ids[]=" + i).then(response => {
sa[i] = response.data.data;
for (var i = 0; i < sa.length; i++) {
this.season_averages.push(sa[i]);
}
});
}
console.log(season_averages);
}
},
mounted() {
this.getSeasons();
this.getAllPlayers();
}
});
So I'm making request to get data of nba players and teams. In this script, my first function returns a json structure containing only the id of the players. The second one is supposed to return saisons averages of all the players. However, you can only access the stats of a specific players.
I mean, you can access the stats players in function of his id passed in parameter of the url.
Example : https://www.balldontlie.io/api/v1/season_averages?player_ids[]=237
This url display the saisons averages of the player having the id equal to 237.
So what I want to do is to get all of them and to do that I have to get the ID of all the players. This is why I need the first function. I'm gonna use it in the second function to concate each id with the url of the api to. So I can access and store all of them into an array and return all there stats.
My question is how can I use for loop on axios request to get seasons avergages of each players ?
Regards
YT

You can use async/await for this issue.
Let’s say we have an array of fruits we want to get from the fruit basket.
const fruitsToGet = ['apple', 'grape', 'pear']
We simply create a function that returns a specific fruit after some time:
const sleep = ms => {
return new Promise(resolve => setTimeout(resolve, ms))
}
const getNumFruit = fruit => {
return sleep(1000).then(v => fruitBasket[fruit])
}
We are going to loop through this array.
const forLoop = async _ => {
console.log('Start')
for (let index = 0; index < fruitsToGet.length; index++) {
// Get num of each fruit
}
console.log('End')
}
In the for-loop, we will use getNumFruit to get the number of each fruit. We’ll also log the number into the console.
Since getNumFruit returns a promise, we can await the resolved value before logging it.
const forLoop = async _ => {
console.log('Start')
for (let index = 0; index < fruitsToGet.length; index++) {
const fruit = fruitsToGet[index]
const numFruit = await getNumFruit(fruit)
console.log(numFruit)
}
console.log('End')
}
When you use await, you expect JavaScript to pause execution until the awaited promise gets resolved. This means awaits in a for-loop should get executed in series.
The result is what you’d expect
This behaviour works with most loops (like while and for-of loops)…
But it won’t work with loops that require a callback. Examples of such loops that require a fallback include forEach, map, filter, and reduce. We’ll look at how await affects forEach, map, and filter in the next few sections.
Source

As far as I can see - you are writing asynchronous code and expecting synchronous execution. When you homework is to include these two functions - then they both should return Promises.
If you just want to stick some stats to a player - a simpler approach could work, too.
const playerurl = 'https://www.balldontlie.io/api/v1/players?search=LeBron';
const statsurl =
'https://www.balldontlie.io/api/v1/season_averages?player_ids[]=';
axios.get(playerurl).then(playerData => {
const statsPromises = playerData.data.data.map(function(player) { // Use Array.map to create promises
return axios.get(statsurl + player.id).then(function(result) {
return new Promise(function(resolve) { // Chain promises
player.stats = result.data.data;
resolve(player); // Finally resolve the player and stats
});
});
});
Promise.all(statsPromises).then(function(playerStats) {
console.log(JSON.stringify(playerStats));
});
});
[
{
"id": 237,
"first_name": "LeBron",
"height_feet": 6,
"height_inches": 8,
"last_name": "James",
"position": "F",
"team": {
"id": 14,
"abbreviation": "LAL",
"city": "Los Angeles",
"conference": "West",
"division": "Pacific",
"full_name": "Los Angeles Lakers",
"name": "Lakers"
},
"weight_pounds": 250,
"stats": [
{
"games_played": 52,
"player_id": 237,
"season": 2019,
"min": "34:53",
"fgm": 9.58,
"fga": 19.52,
"fg3m": 2.12,
"fg3a": 6.15,
"ftm": 3.85,
"fta": 5.52,
"oreb": 0.96,
"dreb": 6.75,
"reb": 7.71,
"ast": 10.73,
"stl": 1.27,
"blk": 0.46,
"turnover": 3.98,
"pf": 1.71,
"pts": 25.12,
"fg_pct": 0.491,
"fg3_pct": 0.344,
"ft_pct": 0.697
}
]
}
]
Maybe there is more to be done - that's where you and homework come in!

Related

Undefined when searching with Find Mongoose Express

I receive an array of products that I need to search for with Mongoose, so I use Find passing the _id of each product. But when going through the array and trying to search, I only receive the data of the first product, for the others I always receive undefined.
This is my code:
const calc = async (details) => {
let grandSubtotal = 0; }
console.log(details);
for (let i = 0; i < details.length; i++) {
let verifyProduct = await Product.find({ _id: details[i]._id});
console.log(verifyProduct[i].salePrice); //It only shows the first product, the others are undefined
.......
}
In my MongoDB database I have the products all saved with salePrice always, in this way:
{
"_id": "628fa841cde1d960c675ee24",
"barCode": "0000075053765",
"idProduct": "03",
"name": "MALBORO ARTESANAL 20",
"desc": "PAQUETE 20 CIGARROS",
"presentation": "PIECES",
"salePrice": 550,
"purchasePrice": 526,
"stock": 0,
"available": true,
"img": [],
"status": false
}
How can I obtain the salePrice information of all the products that I receive since for now I only receive information on the first one and the others are always undefined?
this because, you are using .find({})
let verifyProduct = await Product.find({ _id: details[i]._id});
You're querying using .find({ _id: details[i]._id}), you will always have result in the form of [{..onevalue at 0..}] because .find() returns result in an []
so, when you execute the loop for the first time, your i will be 0 and so when you access the verifyProduct[0].salePrice it will have value. But when your i become 1, your verifyProduct will still have result at 0 position only.
Fix:
const calc = async (details) => {
let grandSubtotal = 0;
console.log(details);
for (let i = 0; i < details.length; i++) {
let verifyProduct = await Product.findById(details[i]._id);
// no array, so access it directly
console.log(verifyProduct.salePrice);
}
}
since you are querying by _id, you can use .findById({}) instead of .find().
Database query in a for loop is super inefficient. You should use the $in MongoDB operator to select multiple documents at once.
Example
const arrayOfIds = ["631318a217f73aa43a58855d", "63132ba7525da531e171c964"];
Product.find({ _id: { $in: arrayOfIds }});

How do I merge two downloaded arrays into a single JSON array?

I'm pretty new to learning to code. So i might get a lot of basics wrong.
Basically i am downloading API content from two different accounts via request-promise and want to merge them into a bigger array. I'm struggling with escaping my local data from the request-promise function and also combining it with the second array
Here's what i got so far:
//request the site and do some stuff with the data
rp(rpOptions)
.then(function (parsedBody) {
let incomingData1 = (parsedBody); //turning data into a value to change it a little
incomingData1.forEach((incomingData1) => {incomingData1.yearsRetired = 0}); //to add a new property
incomingData1 = JSON.stringify(parsedBody, ["favFood", "age", "work", "yearsRetired"], 2); //to filter only relevant properties into a JSON thing (i eventually want to save it to a txt file)
});
i'd then do the same for the second account and then try to get that data outside of the function and merge it into a single array so that it looks like this:
{
"first_account_name": {
"individual1": {
"favFood": 'fries',
"age": 23,
"work": 'astronaut'
"yearsRetired": 0
},
"individual2": {
"favFood": 'banana',
"age": 55,
"work": 'zookeeper'
"yearsRetired": 0
{
...
}
},
"second_account_name": { ... }
"individual6": {
"favFood": 'apple',
"age": 49,
"work": 'dinosaur'
"yearsRetired": 0
"individual7": {
"favFood": 'sausage',
"age": 33,
"work": 'doctor'
"yearsRetired": 0
{
...
}
how do i get my data into a variable outside of rp? and how do i set it up so that it ends up like a nested array?
Thanks a lot and sorry for being confusing :P
What you are looking for is a global array that gets data pushed into it on every Promise request called right. So firstly, create a simple array and place it on top of the page or if you are using a class just insert it into the respective fields.
Let accountDetails = [];
Next, inside then function call this variable like so,
rp(rpOptions)
.then(function (parsedBody) {
let incomingData1 = (parsedBody);
incomingData1.forEach((incomingData1) => {incomingData1.yearsRetired = 0});
incomingData1 = JSON.stringify(parsedBody, ["favFood", "age", "work", "yearsRetired"], 2);
accountDetails.push({
"individual1" : incomingData1
})
});
If you're using ES6
const processData = (data) => {
return data.map((item) => ({
favFood: item.favFood,
age: item.age,
work: item.work,
yearsRetired: 0
}))
}
// any value returned by then will be wrapped in promise
// and can be `await` ed
// you can also use
// const [ data1, data2 ] = await Promise.all([
// rp(requestOption1).then(data => processData(data)),
// rp(requestOption2).then(data => processData(data))
// ])
// if you want it to be executed parallely
const data1 = await rp(requestOption1).then(data => processData(data));
const data2 = await rp(requestOption2).then(data => processData(data));
const mergedData = [
...data1,
...data2
];
If you don't have async await
const processData = (data) => {
return data.map((item) => ({
favFood: item.favFood,
age: item.age,
work: item.work,
yearsRetired: 0
}))
}
Promise.all(
rp(requestOption1).then(data => processData(data)),
rp(requestOption2).then(data => processData(data))
).then(results => {
const mergedData = results.reduce((collection, result) => {
return collection.concat(result);
}, []);
})
Note:
I wrote the function name processData because I don't know what is being processed. I suggest you to be more specific on the function name. (e.g. what it does)

Getting a array as a result when the result should be an Map?

I am trying to use MAP into node.js program and I am getting final result however its in array but I need in map. I have write some code to use map but its not working as expected.
Please find below program
function CreateProduceMVPRateAsset(data, callback) {
var ProducePRICE = {};
var MVPPRICE = [];
var MVPPRICE_BS = {};
var MVPPRICE_LB = {};
const walletPath = path.join(process.cwd(), 'wallet');
const wallet = new FileSystemWallet(walletPath);
console.log(`Wallet path: ${walletPath}`);
console.log('Data', data);
console.log('Username', data.username);
var PRODUCENAME = data.PRODUCE;
var COUNTRY = data.COUNTRY;
var STATE = data.STATE;
var MVPRATES = data.MVPRATES;
console.log('MVPRATERATE', MVPRATES);
// here I need help
const MVPRATE = new Map(MVPRATES);
for (const [k, v] of MVPRATE.entries()) {
console.log('Inside map', k, v);
MVPPRICE = v.RATE; // should go in MVPPRICE
var Value = MVPPRICE[0].value; // want to get first element value from MVPPRICE array
console.log('Value', Value);
var value_lb = Value / 40;
console.log('value_lb', value_lb);
value_lb = Number(value_lb.toFixed(4));
console.log('If the value of BS provided controller come here');
MVPPRICE_LB.Value = value_lb;
MVPPRICE_LB.QuantityUnit = 'LB';
MVPPRICE_LB.uidisplay = false;
MVPPRICE_LB.CurrencyUnit = 'USD';
MVPPRICE.push(MVPPRICE_LB);
ProducePRICE.MVPPRICE = MVPPRICE;
ProducePRICE.PRODUCENAME = PRODUCENAME;
ProducePRICE.STATE = STATE;
ProducePRICE.COUNTRY = COUNTRY;
console.log('ProducePRICE', ProducePRICE); // whatever result getting it should be map however getting in array.
}
}
JSON structure which I am sending using postman
{
"username": "admin2",
"PRODUCE": "Apple",
"STATE": "MI",
"COUNTRY": "US",
"MVPRATES": {
"fuji": {
"VARIETY": "fuji",
"RATE": [
{
"UNIT": "Bussel",
"CURRENCY": "USD",
"VALUE": 10.25,
"UIDISPLAY": true
}
]
},
"gala": {
"VARIETY": "gala",
"RATE": [
{
"UNIT": "Bussel",
"CURRENCY": "USD",
"VALUE": 10.25,
"UIDISPLAY": true
}
]
}
}
}
Output which I am getting:
#SudhakarRS is right, you're getting your MAP back, you're just getting your values in an array for MVPRATE, and it almost looks like that was your intention, seeing you push here:
MVPPRICE.push(MVPPRICE_LB);
I mentioned in the comments, but I think what's happening is the Map doesn't apply recursively, so when it sees MVPPRICE, it's treating your value array, as the value instead of part of the map.
I think what you're trying to do can be solved by this:
MVPPRICE.concat(MVPPRICE_LB);
ProducePRICE.MVPPRICE = new Map(MVPPRICE)

looping through 2 Json files with each 200k records in them

I have 2 large json files with each 200k objects and when i try to loop between 2 jsons for a common id, it takes more time for execution.
Implementation 1
for (var i in matterData.data) {
const fobj = matterData.data[i];
const ma_array = [];
for (var j in activityData.data) {
const aobj = activityData.data[j];
if (fobj.id === aobj.matter.id) {
ma_array.push(aobj);
}
if (ma_array.length > 0) fobj.activities = ma_array;
}
}
Implementation 2
for (var i in matterData.data) {
//Activities
matters_array = [];
matters_array = activityData.data.filter(function (el) {
if (el.matter !== null) return el.matter.id == matterData.data[i].id;
});
if (matters_array.length > 0) matterData.data[i]["activities"] = matters_array;
}
Implementation 3
for (var i in matterData.data) {
matters_array = [];
for (var j in activityData.data) {
if (activityData.data[j]["matter"] !== null) {
if (matterData.data[i].id === activityData.data[j]["matter"].id) {
matters_array.push(activityData.data[j]);
}
if (matters_array.length > 0) matterData.data[i]["activities"] = matters_array;
}
}
}
Every implementation is taking more time to execute.
ActivitiesData will have an id and also matter.id which is related to matterData.id
Any insights , please help
Matters Data
var matterData= {
"data": [
{
"id": 1055395769,
"description": "Peters",
"status": "Pending",
"location": null,
"client_reference": "1532",
"billable": true,
"billing_method": "hourly",
"open_date": "2019-06-05",
"close_date": null,
}
]
};
Activities Data
var activityData = {
"data": [
{
"id":285568423,
"type": "ExpenseEntry",
"date": "2011-01-01",
"quantity_in_hours": 1,
"rounded_quantity_in_hours": 1,
"quantity": 1,
"rounded_quantity": 1,
"price": 100,
"matter": {
"id": 1055395769
}
},
{
"id": 285568428,
"type": "MonEntry",
"matter": {
"id": 1055395769
}
},
{
"id": 285568442,
"type": "EEntry",
"matter": {
"id": 1055395769
}}]
};
First create a map containing id as the key and matter.data as value and create a empty array for acitvity then iterate over the activity and push activity in empty array
const map = {}
for( var i in matterData.data){
map[matterData.data[i].id] = matterData.data[i];
matterData.data[i].activities = [];
}
for(var i in activityData.data){
var matter = map[activityData.data[i].matter.id];
matter.activities.push(activityData.data[i]);
}
for( var i in matterData.data){
console.log(matterData.data[i]);
}
This solution works only if matterData.data[i].id is unique
You are running an O(n^2) loop which is nearly 200000*200000 loop. Thats huge calculation to handle. You can reduce this complexity by using map. Store all values of activityData.data in map with id. And then iterate over one matterData.data to check if the id exists in map.
Implementation of the answer with ruhul
const map = Object.create(null);
const res = [];
// Store all values of matterData.data in map with id.
matterData.data.forEach(item => (map[item.id] = item));
// iterate over one activityData.data to check if the id exists in map.
activityData.data.forEach(item => {
const { id } = item;
if (map[id]) {
res.push(item);
}
});
The Set.has is O(1) which, I think can be used efficiently for this purpose.
var subSet = new Set(activityData.data.map(obj => {obj.matter.id}));
for (var i in matterData.data) {
const ma_array = [];
if (subSet.has(matterData.data[i].id){
...
}
}

For loop with Node js promise chaining

I am very new to Node js and asynchronous programming seems difficult for me to grasp. I am using promise-mysql to make the flow synchronous but I have hit a road block with for loop inside of a chain of promise
I have a multiple choice question module. One table stores all the mcq questions and the other stores all the related choices for the questions. I am using the output of the first query as an input to the second query and so I did promise chaining as below
var mcqAll=[]
var sql_test_q_ans='select qId, q_text from questions'
con.query(sql_test_q_ans)
.then((result)=>{
for(var i=0; i<result.length; i++)
{
ques=result[i]
var sql_test_q_ops='SELECT op_text, op_id FROM mc_ops WHERE
q_id='+result[i].q_id
con.query(sql_test_q_ops)
.then((resultOps)=>{
mcqAll.push({i: ques, ops: resultOps})
console.log(mcqAll)
})
}
})
I am trying to create a javascript object array which would look something like this
[{q_text:'How many states in USA', q_ops:{1:25, 2:35, 3:45, 4:50}}
{question2 and its options}
{question3 and its options}....
]
When I run the above code the object populates all the question's options correctly but the same question is repeated in all the q_text for all questions.
[ { q_text: 'No of states in USA',
[ {op_text: '25', mc_op_id: 113 },
{ op_text: '35', mc_op_id: 114 },
{ op_text: '45', mc_op_id: 115 },
{ op_text: '50', mc_op_id: 116}],
{ q_text: 'No of states in USA',
[ {op_text: 'A', mc_op_id: 1 },
{ op_text: 'B', mc_op_id: 2 },
{ op_text: 'C', mc_op_id: 3 },
{ op_text: 'D', mc_op_id: 4}],
{ q_text: 'No of states in USA',
[ {op_text: 'Yes', mc_op_id: 31 },
{ op_text: 'No', mc_op_id: 32 },
{ op_text: 'No sure', mc_op_id: 33 },
{ op_text: 'Might be', mc_op_id: 34}]
]
I feel like it has something to do with asynchronous flow since console.log before the second query gets printed in all before printing anything after the second query. Any insight would be appreciated
Edit: I added a sample output for better understanding. As seen in the output, the options change and get stored in the js object in the for loop but the question is updated for all the objects to the last question in the for loop
node js current working async and await, still now use to async and await,
use this reference url: https://javascript.info/async-await
async and await is work as promise, await is use to wait to execute script
example
let mcqAll=[]
let sql_test_q_ans='select qId, q_text from questions'
async function showAvatar() {
let result = await con.query(sql_test_q_ans);
if(result.length > 0){
array.forEach((async function (item, index, result) {
let q = result[index];
let sql_test_q_ops='SELECT op_text, op_id FROM mc_ops WHERE
q_id='+result[index].q_id
let executeQuery = await con.query(sql_test_q_ops);
if(executeQuery.affectedRows > 0){
mcqAll.push({index: q, ops: executeQuery})
console.log(mcqAll);
}
});
}
}
You have a scope problem here
This is an example to reproduce your problem:
ques is a global variable that is updated in the for-loop so, when the async code ends the execution will read the global variable with the last ques = result[i] value.
'use strict'
const result = ['a', 'b', 'c']
const mcqAll = []
var ques
for (var i = 0; i < result.length; i++) {
ques = result[i]
var sql_test_q_ops = 'SELECT op_text, op_id FROM mc_ops WHERE q_id = ' + result[i].q_id
query(sql_test_q_ops)
.then(() => {
mcqAll.push({ i: ques })
console.log(mcqAll)
})
}
function query() {
return new Promise(resolve => setTimeout(resolve, 100))
}
But, if you simply declare the ques like this:
for (var i = 0; i < result.length; i++) {
const ques = result[i]
const sql_test_q_op...
all will work.
It is a good practice to use const or let instead of var because the last one creates a global scoped variable that is dangerous.
Regarding your comment: the output is empty because this for-loop is sync, so you reply in sync way to the response.
An example on how to manage this case could be like this:
'use strict'
const result = ['a', 'b', 'c']
const mcqAll = []
const promiseArray = result.map(ques => {
const sql_test_q_ops = 'SELECT op_text, op_id FROM mc_ops WHERE q_id = ' + ques.q_id
return query(sql_test_q_ops)
.then(() => { mcqAll.push({ i: ques }) })
})
// Wait for all the query to complete before rendering the results
Promise.all(promiseArray)
.then(() => {
console.log({ mcqAll });
res.render('mcqAllPage', { mcqAll })
})
.catch(err => res.send(500)) // this is an example
function query() {
return new Promise(resolve => setTimeout(resolve, 100))
}
Consider that there are many possibilities to implement this:
use for async iterator to run query sequentially
improve performance by run only one query with a in condition instead of a query for each q_id and manage the result with some code to group the results
using the promise array as in the example
Go deeper and choose the one that fits best for your need.
Important: .catch always the promise chain!

Categories