For-loop order (with asynchronous function) - javascript

I have this code :
exports.formatDepArrTables = function(jsonReturn, type, placeName, callback) {
let toSend = "";
if (type == 'departure') {
for (let j = 0; j < jsonReturn.departures.length; j++) {
console.log("DEPARTURES LOOP j = " + j + "/" + jsonReturn.departures.length);
if(currentDeparture.display_informations.links[0] === undefined) {
toSend += ""; // setting some string informations
}
else {
let oceTrainId = ""; // random number given by a json
_this.getDisruptionFromDisruptionId(oceTrainId, function(data) {
for(let i = 0; i < data.disruptions[0].impacted_objects[0].impacted_stops.length; i++) {
if(currentImpactedStop.stop_point.label == placeName) {
toSend += "string";
}
}
});
}
}
console.log("End of the first loop");
return callback(toSend);
} else if (type == 'arrival') {
// copy&paste
} else {
throw new Error("not defined");
}
};
When I run this code (I use NodeJS), it makes 1/10, 2/10... from the first loop but it didn't iterate the second loop on the 1st iteration of the first loop (and it shows "End of the first loop" and starts the second loop).
getDisruptionFromDisruptionId is a custom method which makes a request (with 'request' NodeJS module) on an API.
Of course, I need to have informations given by getDisruptionFromDisruptionId to run my next loop...
Parent function of this code part is returning a callback that needs to be "filled" at the end of the both two loops.
Any ideas ?

request is async function, you need to add async / await to your code or use recursion

Related

Why are these variables not maintaing value?

I have two problems i cant figure out. When i call GetParams first to get used defined values from a text file, the line of code after it is called first, or is reported to the console before i get data back from the function. Any data gathered in that function is null and void. The variables clearly are being assigned data but after the function call it dissapears.
let udGasPrice = 0;
let udGasLimit = 0;
let udSlippage = 0;
I want to get data from a text file and assign it to variables that need to be global. able to be assigned in a function but used outside it. So above is what i was doing to declare them outside the function. because if i declare them inside, i lose scope. It doesnt seem right to declare with 0 and then reassign, but how else can i declare them gloabaly to be manipulated by another function?
next the code is called for the function to do the work
GetParams();
console.log('udGasPrice = " + udGasPrice );
The code after GetParams is reporting 0 but inside the function the values are right
The data is read and clearly assigned inside the function. its not pretty or clever but it works.
function GetParams()
{
const fs = require('fs')
fs.readFile('./Config.txt', 'utf8' , (err, data) => {
if (err) {
console.error(err)
return;
}
// read file contents into variable to be manipulated
var fcnts = data;
let icnt = 0;
for (var x = 0; x < fcnts.length; x++) {
var c = fcnts.charAt(x);
//find the comma
if (c == ',') {
// found the comma, count it so we know where we are.
icnt++;
if (icnt == 1 ) {
// the first param
udGasPrice = fcnts.slice(0, x);
console.log(`udGasPrice = ` + udGasPrice);
} else if (icnt == 2 ) {
// second param
udGaslimit = fcnts.slice(udGasPrice.length+1, x);
console.log(`udGaslimit = ` + udGaslimit);
} else {
udSlippage = fcnts.slice(udGaslimit.length + udGasPrice.length +2, x);
console.log(`udSlippage = ` + udSlippage );
}
}
}
})
}
Like i said i know the algorithm is poor, but it works.(Im very noob) but why are the variables not retaining value, and why is the code after GetParams() executed first? Thank you for your time.
The code is executed before the GetParams method finishes, because what it does is an asynchronous work. You can see that by the use of a callback function when the file is being read.
As a best practice, you should either provide a callback to GetParams and call it with the results from the file or use a more modern approach by adopting promises and (optionally) async/await syntax.
fs.readFile asynchronously reads the entire contents of a file. So your console.log('udGasPrice = " + udGasPrice ); won't wait for GetParams function.
Possible resolutions are:
Use callback or promise
let udGasPrice = 0;
let udGasLimit = 0;
let udSlippage = 0;
GetParams(() => {
console.log("udGasPrice = " + udGasPrice);
});
function GetParams(callback) {
const fs = require('fs')
fs.readFile('./Config.txt', 'utf8', (err, data) => {
if (err) {
console.error(err)
return;
}
// read file contents into variable to be manipulated
var fcnts = data;
let icnt = 0;
for (var x = 0; x < fcnts.length; x++) {
var c = fcnts.charAt(x);
//find the comma
if (c == ',') {
// found the comma, count it so we know where we are.
icnt++;
if (icnt == 1) {
// the first param
udGasPrice = fcnts.slice(0, x);
console.log(`udGasPrice = ` + udGasPrice);
} else if (icnt == 2) {
// second param
udGaslimit = fcnts.slice(udGasPrice.length + 1, x);
console.log(`udGaslimit = ` + udGaslimit);
} else {
udSlippage = fcnts.slice(udGaslimit.length + udGasPrice.length + 2, x);
console.log(`udSlippage = ` + udSlippage);
}
}
}
callback()
})
}
fs.readFileSync(path[, options]) - it perform same operation in sync - you still need to edit your code accordingly
Also, it's advisable that you don't edit global variables in the function and return updated variables from the function.

Difference of two 2D arrays

I am trying to make function to get me difference of two 2D arrays but I found that to make function removeArray() work it's required to take different counter variables in both function. If it's taken i in both than loop iterate only once where it should iterate twice.
function removeArray(toremove, myarray){
for(i=0; i< toremove.length ; i++){
// console.log(getIndex(toremove[i],myarray));
myarray.splice(getIndex(toremove[i],myarray),1);
console.log("" + myarray); //only [2,3] will get remove
}
}
function getIndex(array, myarray){
for(i=0;i< myarray.length ; i++){
// if(typeof(array)== 'undefined'){console.log("error"); return 100;}
if((myarray[i][0] == array[0]) && (myarray[i][1] == array[1])){
return i;
}
}
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
removeArray(toremove,myarray);
Also when commented parts are included(both together) i.e, // console.log(getIndex(toremove[i],myarray)) and // if(typeof(array)== 'undefined'){console.log("error"); return 100}it iterates infinitely where it should have not more than twice.
Why is it so? Pls help. Thanks in advance!
The problem is that you do not define i with var or let. In that case i is a global variable and is shared by the two functions.
So when the nested getIndex function is called, i potentially increments until myarray.length. Then when execution comes back inside the first function's loop, i is already too great to continue looping. The loop there exits and all is done.
Instead define i as a local function variable (var) or block variable (let) and it will work:
function removeArray(toremove, myarray) {
for(let i = 0; i < toremove.length; i++) {
myarray.splice(getIndex(toremove[i], myarray), 1);
}
}
function getIndex(array, myarray){
for(let i = 0; i < myarray.length; i++){
if (typeof(array)== 'undefined') {
console.log("error");
return 100;
}
if ((myarray[i][0] == array[0]) && (myarray[i][1] == array[1])) {
console.log("found match at position " + i);
return i;
}
}
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log("before: " + JSON.stringify(myarray));
removeArray(toremove,myarray);
console.log("after: " + JSON.stringify(myarray));
Usually the better practice is to not mutate an array with splice, but to return a new copy without the items to be removed. You can use filter and every for that. And then you must assign the function's return value to the array that should have the result (could also overwrite the same array):
function removeArray(toremove, myarray){
return myarray.filter(arr =>
toremove.every(rem => arr[0] != rem[0] || arr[1] != rem[1])
);
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log("before: " + JSON.stringify(myarray));
myarray = removeArray(toremove, myarray);
console.log("after: " + JSON.stringify(myarray));
Maybe .filter method be good for you
function removeArray(toremove, myarray) {
return myarray.filter((el) => {
for (let i in toremove) {
if (toremove[i][0] === el[0] && toremove[i][1] === el[1]) {
return false;
}
}
return true;
});
}
var myarray=[[1,1],[1,2],[1,3],[1,4],[2,1],[2,2],[2,3],[2,4],[3,1],[3,2],[3,3],[3,4],[4,1],[4,2],[4,3],[4,4]];
var toremove=[[2,3],[3,3]];
console.log(removeArray(toremove,myarray));
It is iterating once because your code is getting into an error. javascript always passes variable by reference. You can refer this to understand
Uncaught TypeError: Cannot read property '0' of undefined on line 16
you can use below logic to avoid error
function removeArray(toremove, myarray){
let indexes = []
for(i=0; i < toremove.length ; i++){
indexes.push(getIndex(toremove[i],myarray))
}
for (var i = indexes.length -1; i >= 0; i--)
myarray.splice(indexes[i],1);
}

NodeJS require with asynch functions when synch is wanted

I have the following code
var utils = require(`${__dirname}/../../utils/utils.js`);
...
let object = utils.parse(input);
if (object === undefined){
let helper = utils.recognize(input);
msg.channel.sendMessage("\"" + input + "\" not recognized. Did you mean \"" + helper[0] + "\"?");
object = utils.parse(helper[0]);
}
//code related to object
console.log(object.strLength);
where "parse" tries to match the input to an object in a database, and "recognize" tries to find the best match if the input is spelled incorrectly (Levenshtein) (along with additional info such as how close the match was).
Currently the issue is that the code is ran asynchronously; "object.strLength" returns an undefined before utils.recognize() returns a value. If I copy/paste the recognize() and parse() functions into the file, then the code is run synchronously and I do not run into any issues. However I would rather keep those functions in a separate file as I reuse them in other files.
Is there a way to specify that the functions in utils must be synch? I know that there are libraries that convert asynch into synch but I prefer to use as few libraries as I can help it. I tried to have the recognize functions return a Promise but it ended up as a jumbled mess
edit: here's parse. I did not think it was necessary to answer this question so I did not include it initially:
var db = require(`${__dirname}/../data/database.js`);
...
var parse = (input) => {
let output = db[output];
if (output === null) {
Object.keys(db).forEach((item) => {
if (db[item].num === parseInt(input) || (db[item].color + db[item].type === input)){
output = db[item];
return false;
}
});
}
return output;
}
I solved the issue, thanks everyone. Here's what was wrong, it was with recognize(). It was my mistake to not show the code for it initially.
Original recognize:
var recognize = (item) => {
//iterate through our databases and get a best fit
let bestItem = null;
let bestScore = 99999; //arbitrary large number
//let bestType = null;
//found algorithm online by milot-mirdita
var levenshtein = function(a, b) {
if (a.length == 0) { return b.length; }
if (b.length == 0) { return a.length; }
// swap to save some memory O(min(a,b)) instead of O(a)
if(a.length > b.length) {
let tmp = a;
a = b;
b = tmp;
}
let row = [];
for(let i = 0; i <= a.length; i++) {
row[i] = i;
}
for (let i = 1; i <= b.length; i++) {
let prev = i;
for (let j = 1; j <= a.length; j++) {
let val;
if (b.charAt(i-1) == a.charAt(j-1)) {
val = row[j-1]; // match
} else {
val = Math.min(row[j-1] + 1, // substitution
prev + 1, // insertion
row[j] + 1); // deletion
}
row[j - 1] = prev;
prev = val;
}
row[a.length] = prev;
}
return row[a.length];
}
//putting this here would make the code work
//console.log("hi");
Object.keys(db).forEach((key) => {
if (levenshtein(item, key) < bestScore) {
bestItem = key;
bestScore = levenshtein(item, key);
}
});
return [bestItem, bestScore];
}
My solution was to move the levenshtein function outside of the recognize function, so if I wanted to I can call levenshtein from another function
#user949300 and #Robert Moskal, I changed the forEach loop into a let...in loop. There is no functional difference (as far as I can tell) but the code does look cleaner.
#Thomas, I fixed the let output = db[output]; issue, oops.
Again, thanks for all of your help, I appreciate it. And happy New Year too

how to get incremented value in for loop after callback function in javascript?

My Requirement:
I want to get the list of values using for loops. In for loop one iteration completed one time then the callback will send that list of values(array).
Once the first iteration completed second time loop value should be get incremented value.
For example : 5 values
after 5th iteration then loop is over. then second time loop should start with '0' but here it's starting with last incremented value. please help me to achieve this.
Below code is working fine for the first time.
Callback function:
$inventoryManagement.getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId(objectId,attributeId, function(objectAttributeBlockElement) {
//$scope.val = myOwnJ;
console.log(objectAttributeBlockElement);
});
Function:
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
for (;myOwnJ < pageObject.objects[i].objectAttribute.length;) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
You could pass j as an additional function parameter, such as
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId, attributeId, j, callback) {
so it won't be a local variable. Then, instead of declaring it locally, use the following:
for (j = ((j === null) ? 0 : j); j < pageObject.objects[i].objectAttribute.length; j++) {
That way, if you call your function with j, you'll get it incremented after each call.
Another approach, which I won't recommend, would be making j a global variable by declaring it ouside your function instead of passing it as a parameter. That way you don't have to modify your function declaration at all. If you're up to that, I strongly suggest modifying the variable name cause j would be too generic for a global scope variable and it will cause trouble sooner or later: use something like myOwnJ and you'll be fine.
EDIT: Full source code (as requested by the OP):
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
if(myOwnJ < pageObject.objects[i].objectAttribute.length) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
else {
myOwnJ = 0;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
What you are looking for is a global variable for 'j'. Although this is discouraged to be used.
var j=0;
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId =
function(objectId, attributeId, callback) {
//do your stuff
//increment j
j++;
}

Find variable value

I have some inputs in my app: <_input code/> + <_input code/> = <_input code/>.
Let's imagine first input name is a, appropriately second and third inputs' names are b and c. I filled my inputs:
7 + x = 12
Is there any way to calculate x value?
What do I want from my script:
It finds variable in inputs' values.
It checks all fields of my form filled properly.
It finds variable in inputs' values.
From given information script calculates value of variable.
How many inputs will be doesn't matter. I just want to find value of x. Is there any library to do this?
function calculcateA(b,c){
return c-b;
}
if(inputA === 'x'){
alert(calculateA(inputB,inputC));
}
And so on... there is nothing wrong with this functions, but I want to automate this proccess like WolframAplha.
The best thing for you, I guess would be to find some library for solving equations. If you are in need to solve bigger sets of equations then maybe something related to linear algebra.
Can't really tell you an exact solution so you will have to search for yourself.
Here is some code that should solve the problem.
function calculate() {
var varIndex = -1;
//Ensure that at least two three arguements are passed
if (arguments.length < 3) {
throw "You need at least three parameters to make an equation";
}
//Make sure that there is only one variable
for (var i = 0; i < arguments.length; i++) {
if (isNaN(arguments[i])) {
if (varIndex != -1) {
throw "You can't have two variables";
return;
}
varIndex = i;
}
}
//If variable has been found
if (varIndex != -1) {
var answer = 0;
//If variable is at the last position, add all constants
if (varIndex == types.length - 1) {
for (var j = 0; j < arguments.length - 1; j++) {
answer = answer + j;
}
} else {
//Otherwise Deduct all values from the last
answer = arguments[arguments.length - 1];
for (var k = 0; k < arguments.length - 1; k++) {
if (k == varIndex) { continue; }
answer = answer - j;
}
}
//Return Result
return { variable: arguments[varIndex], answer: answer };
}
else {
throw "You need at least one variable";
return;
}
}
You would use the above as follows:
var a = document.querySelector("input[name=a]");
var b = document.querySelector("input[name=b]");
var c = document.querySelector("input[name=c]");
var calcBtn = document.getElementById("calculate");
calcBtn.addEventListener("click", function () {
try {
var result = calculate(a.value, b.value, c.value);
console.log("The value of " + result.variable + " is " + result.answer);
} catch (e) {
console.log(e);
}
});

Categories