Node.js passing variable to parent function - node.js

So i ran into a problem. I don't know how to pass single string to the parental function from a child function and then pass that string as a response to the client side.
This whole thing gets five recent matches from API and then checks for a win or a loss depending on the player name.
Question 1: as i said before i don't know how to pass string from a child function to the parental function and then send it as a response to client side.
Question 2: the output of this should be WWWLW and how i think it should be ordered like that. But every time it outputs in different order like LWWWW WLWWW and so on... it has good arguments but different order and i am missing something here.
code:
var request = require('request');
app.get('/history',getmatches, getwins);
function getmatches(req, res, next){
var match = {};
request({
url: "https://eun1.api.riotgames.com/lol/match/v3/matchlists/by-account/"+ID+"/recent?api_key=" + key,
json: true
}, function (error, res) {
if (!error && res.statusCode === 200) {
for(var i=0; i < 5; i++){ //getting ID's of five last matches
match[i] = res.body.matches[i].gameId;
}
req.somevariable = match;
next();
}
}
);
};
function getwins(req, res, callback){
var match = req.somevariable;
var streak = '';
var pending = 0;
for( i = 0; i < 5; i++){ // passing ID's to another api link to get single match data
request({
url: "https://eun1.api.riotgames.com/lol/match/v3/matches/"+match[i]+"?api_key=" + key,
json: true
}, function(req,res, body){
for(var j = 0; j < 10; j++){ //looping through 10 players in a match to find specific one
if(body.participantIdentities[j].player.summonerName == nickname){
if( body.participants[j].stats.win == true){
streak += 'W';
}else{
streak += 'L';
}
}
}
if(pending == 4){
console.log(streak); // need this to pass to parent function
return callback(null, streak); // is this something i need ?
}
pending++
});
}
// res streak string to client.js
};

There is solution to process all results when it done. The result variable have all results use any appropriate key instead of url;
function getwins(req, res, callback){
var match = req.somevariable;
var streak = '';
var pending = 0;
var results = {};
var total = 5;
for( i = 0; i < total; i++){ // passing ID's to another api link to get single match data
var url = "https://eun1.api.riotgames.com/lol/match/v3/matches/"+match[i]+"?api_key=" + key;
request({
url: url,
json: true
}, function(req,res, body){
for(var j = 0; j < 10; j++){ //looping through 10 players in a match to find specific one
if(body.participantIdentities[j].player.summonerName == nickname){
if( body.participants[j].stats.win == true){
streak += 'W';
}else{
streak += 'L';
}
}
}
console.log(streak); // need this to pass to parent function
results[url] = streak;
if( total == Object.keys(results).length ) {
// here all requests are done - do with all result what you need
console.log( results );
}
return callback(null, streak); // is this something i need ?
}
});
}
// res streak string to client.js
};

Related

How can I restructure my loop to paginate faster right now it takes too long?

Per_page limit is 100 & I needed the function to be able to find results for any date range. So I added a do while loop and this is what I ended up with:
async function foo(repoOwner, repository, startingDate, endingDate){
const listOfUserObjects = [];
let pages, page = 1;
do{
await axios({
method: 'get',
url : `https://api.github.com/repos/${repoOwner}/${repository}/pulls?state=all&per_page=100&page=${page}`,
}).then(response => {
const users = response.data, startAt = new Date(startingDate), endAt = new Date(endingDate.valueOf());
endAt.setDate(endAt.getDate() + 1);
if(page === 1) pages = (Math.floor((users[0].number)/100))/2, console.log("collecting data, please wait...");
for(let i = 0; i < users.length; i++) {
const createdDate = new Date(users[i].created_at), updatedDate = new Date(users[i].updated_at), closedDate = new Date(users[i].closed_at), mergedDate = new Date(users[i].merged_at);
if(((createdDate || updatedDate || closedDate || mergedDate) >= startAt) && ((createdDate || updatedDate || closedDate || mergedDate) <= endAt)) {
const userObj = {};
userObj.id = users[i].id;
userObj.user = users[i].user.login;
userObj.title = users[i].title;
userObj.state = users[i].state;
userObj.created_at = users[i].created_at.slice(0,10);
listOfUserObjects.push(userObj);
} else {
continue;
};
}
page++
}).catch(error => {
throw error.response.status === 404 ? Error("Error 404 User or Repo Not Found") : Error(error);
});
} while(page < pages);
console.log(listOfUserObjects);
}
For paging through the data I added a do while loop. I expected it to loop through all the pages and retrieve the relevant data.
The code works, but the do while loop is too slow and it loops through all the pages; instead I would prefer it to stop when the data from the desired date range has been retrieved.
This takes ages to retrieve data. Is there a more efficient way to do this?

Node.js for loop output only last item from JSON

The code below only output the last result, I don't get it. I check if the updateDate item contains 2020-05 both items does and I get only the last one. The loop is not looping :)
const briefing = [
{
"updateDate": "2020-05-05T00:00:00.0Z",
},
{
"updateDate": "2020-05-06T00:00:00.0Z",
},
{
"updateDate": "2020-05-13T00:00:00.0Z",
}
];
let date = new Date();
var formattedYearMonth = date.getFullYear() + '-' + ('0' + (date.getMonth()+1)).slice(-2) + '-';
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
var response = JSON.stringify(briefing[i]);
}
}return response;
}
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
var response = JSON.stringify(briefing[i]); // <==== THIS IS WHERE YOUR PROBLEM LIES
}
}return response;
The loop is actually looping :). But for every run of the loop, you are resetting the value of response.
--EDITED--
For the response to be an array, you need to modify your code as
let response = [];
for (var i = 0; i < briefing.length; i++) {
var jsonDate = briefing[i].updateDate;
if (jsonDate.includes(formattedYearMonth)) {
response.push(JSON.stringify(briefing[i]));
}
}
return response;

Can't set headers after they are sent in get request

router.get('/getpostcode', function(req, res){
console.log(req.query.suburb);
var options = {
url: 'http://apiurl?suburb=' + req.query.suburb + "&state=" + req.query.state ,
headers: {
'auth-key': key
}
}
request(options, callback);
function callback(error, response, body){
if(!error && response.statusCode == 200){
info = JSON.parse(body);
info = info.localities.locality;
if( Object.prototype.toString.call( info ) == '[object Array]' ){
for ( var x = 0 ; x < info.length ; x++ ){
var locx = info[x].location;
var qsuburb = req.query.suburb;
if( locx == qsuburb.toUpperCase() ){
res.send( { 'postcode': info[x].postcode } );
}
}
} else if (Object.prototype.toString.call( info ) != '[object Array]') {
var locx = info.location;
var qsuburb = req.query.suburb;
if ( locx == qsuburb.toUpperCase() ){
res.send( { 'postcode': info.postcode } );
}
}
}
}
});
So, I am trying to request data from an API and then based on that data, I am then sending some data back. My code is as above.
Unfortunately, in the callback function, when I am running a loop to find a specific element that I will then send back to the client, when sending that element to client, I am getting the error as shown in the title.
This does not occur when there is no loop and I simply send one element of the data back.
Any ideas on what this could be?
You are sending response more than one for one request, write res.send or JSON outside the loop.
for (var x = 0; x < info.length; x++) {
var locx = info[x].location;
var qsuburb = req.query.suburb;
var postcodes = [];
if (locx == qsuburb.toUpperCase()) {
postcodes.push({
'postcode': info[x].postcode
});
}
res.json(postcodes);
}
You are reaching the res.send function call at least twice in your flow and this is not allowed. That function must be called once to send the response to the client.
Yeah, As #Rahul said, It is not allowed to send response more than one time for the same request - You must be getting same post code, so you either need to store your postcode in a variable and send it after loop overs or you can use a break. Though it is not advisable to use a break in complex loops in the pretty simple loop you can leverage its usage.
for (var x = 0; x < info.length; x++) {
var locx = info[x].location;
var qsuburb = req.query.suburb;
if (locx == qsuburb.toUpperCase()) {
res.send({
'postcode': info[x].postcode
});
break;
}
}
Or as following:
for (var x = 0; x < info.length; x++) {
var locx = info[x].location;
var qsuburb = req.query.suburb;
vat postcode = = null;
if (locx == qsuburb.toUpperCase()) {
postcode = info[x].postcode
}
}
res.send({
postcode
});

About terminating a dynamically constructed sequence of promises

I wrote a script in Node that iterates over a large MongoDB collection, returning a certain number of documents at a time.
The collection has this simple format:
{
name: 'One',
data: '...'
},
{
name: 'Two',
data: '...'
},
...
I'm doing this job with the Q library, using a sequence of promises that get run one after the other:
'use strict';
var Q = require('q');
var monk = require('monk');
var CHUNK_SIZE = 100;
var LIMIT = 1000;
var collection = monk('localhost/dictionary').get('entries');
var promiseFactory = function (j) {
return function (result) {
if (undefined !== result) { // if result is undefined, we are at the first or last iteration.
if (result.length) {
for (var k = 0, max = result.length; k < max; k++) {
console.log(result[k].name); // print name
// ... do something with the document here...
}
} else { // no more documents, end of the iteration
return; // implicitely returns undefined
}
}
// returns CHUNK_SIZE documents, starting from the j-th document
return collection.find({}, { limit: CHUNK_SIZE, skip: j, sort: { name: 1 }});
};
};
var funcs = [];
for (var i = CHUNK_SIZE; i <= LIMIT; i += CHUNK_SIZE) {
funcs.push(promiseFactory(i));
}
var loop = Q.fcall(promiseFactory(0));
funcs.forEach(function (f) {
loop = loop.then(f);
});
The script works well and does achieve what it was designed to do.
However, I would like to improve it:
I'm hardcoding the number of documents in the collection (LIMIT). I would like to get rid of this variable and let the script detect when to stop.
I have a feeling that this approach may not be the most memory-efficient one. In my code, funcs.forEach() chains a lot of copies of the same function in one shot (to be exact LIMIT/CHUNK_SIZE copies). Since I'm working on a very large collection, I was wondering if there's a way to chain a new function only if there are still documents left, while running through the collection.
I think I found the solution to both problems. It is just a simple addition in promiseFactory() which I have highlighted below. Adding it here in the hope it is useful to someone:
var promiseFactory = function (j) {
return function (result) {
if (undefined !== result) { // if result is undefined, we are at the first or last iteration.
if (result.length) {
for (var k = 0, max = result.length; k < max; k++) {
console.log(result[k].en + ' - ' + result[k].le);
}
} else { // no more entries, end of the iteration
return; // implicitely returns undefined
}
}
///////////////// CHANGE HERE ////////////////////////
return entries.find({}, { limit: CHUNK_SIZE, skip: j, sort: { en: 1 }}).then(promiseFactory(j + CHUNK_SIZE));
///////////////////// END ////////////////////////////
};
};

NodeJS class always return the same value

In NodeJS, I tried to create 2 object of a same class. However, these 2 object are always the same despite having different values. Here is the class.
function reading(){
var readingArr = [];
};
reading.prototype.dbValue = function(counter, limit, type, mIndex) {
db.data.find({ 'type': type }).limit(limit).sort({timestamp:-1}).skip(counter, function(err, docs){
readingArr = [];
if( docs != 'undefined' ){
for(var i=0; i<limit; i++){
readingArr.push(docs[i].measurement[mIndex].value.toFixed(2)); //2 Decimal Placet;
}
}
});
if(typeof readingArr == 'undefined'){
readingArr = [];
}
return readingArr;
};
Here is the object creation.
var spo2 = new reading();
var spo2Arr = spo2.dbValue(0, 5, 'Oximeter', 1);
var temp1 = new reading();
var temp1Arr = temp1.dbValue(0, 5, 'Temperature', 0);
Both spo2Arr and temp1Arr return the same value despite having different value in the database. Example
spo2Arr: 98.00
temp1Arr: 98.00
spo2Arr: 37.91
temp1Arr 37.91
May I know how to create two unique object in NodeJS?
You're performing an asynchronous function call which is not going to complete until some time after dbValue() has finished executing.
Try this:
reading.prototype.dbValue = function(counter, limit, type, mIndex, cb) {
db.data.find({ 'type': type }).limit(limit).sort({timestamp:-1}).skip(counter, function(err, docs){
if (err)
return cb(err);
var readingArr = [];
if (docs !== undefined) {
for (var i = 0; i < limit; i++)
readingArr.push(docs[i].measurement[mIndex].value.toFixed(2));
}
cb(null, readingArr);
});
};
Then you might use it like:
var spo2 = new reading();
spo2.dbValue(0, 5, 'Oximeter', 1, function(err, spo2Arr) {
// check for `err`, if it's falsey, use `spo2Arr`
});
var temp1 = new reading();
temp1.dbValue(0, 5, 'Temperature', 0, function(err, temp1Arr) {
// check for `err`, if it's falsey, use `temp1Arr`
});
If the temperature readings depend on the oximeter readings, you'll have to mode the temperature reading code inside the oximeter reading callback or you can use a module like async to help structure your control flow.

Resources