This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 8 years ago.
I'm working on a project using node.js. I'm pulling in a chunk of JSON data and I need to schedule jobs to write data out a serial port based on the data received. I'm using node-schedule for this task. There are several jobs that need to be scheduled and the command for each one is different. What I'm finding is that the arguments passed to the function within the scheduleJob call are interpreted at execution time rather than when scheduled within the for loop. Any ideas on how I can get the string argument to be constant?
Code Example:
var schedule = require('node-schedule');
var schedules[];
…
var startrule = new schedule.RecurrenceRule();
var start_hour = thejson.start_hour;
var start_minute = thejson.start_minute;
for (var k = 0; k < thejson.zones.length; k++)
{
//start
startrule.hour = start_hour;
startrule.minute = start_minute;
var start = schedule.scheduleJob(startrule, function(){
writeSerial('//S' + thejson.zones[k].number + ',1');
});
schedules.push(start); //upon next json update, old schedules in array are cancelled.
}
When it executes, thejson.zones[k].number fails because k is unknown. How can I get that string argument to writeSerial() to be static/constant? I've tried sticking it in an array and passing an index to writeSerial within the schedule, but that index variable is also interpreted at schedule execution.
You have to wrap the establishment of the scheduled job in a function to provide a per-job unique lexical scope:
var start = schedule.scheduleJob(startrule, function(number){
return function() {
writeSerial('//S' + number + ',1');
};
}(thejson.zones[k].number));
The parameter "number" for that anonymous and immediately-invoked function ensures that the scheduled function it returns will be working with a safe copy of that part of your data structure.
Without that extra layer, each one of those jobs you scheduled shared the same variable "k". The loop in which "k" is used ends when "k" is equal to the length of the list, and so each job (when it is eventually invoked as scheduled) sees "k" pointing to a spot beyond the end of the array.
My preferred solution would be to create a partial function:
var start = schedule.scheduleJob(startrule, function(number) {
writeSerial('//S' + number + ',1');
}.bind(null, thejson.zones[k].number);
This creates an anonymous function which has its first argument already bound to it.
Related
This question already has answers here:
How to efficiently randomly select array item without repeats?
(14 answers)
Closed 1 year ago.
I am working on a random generator made using javascript for a html page I am working on, and I have been able to make a function that uses math.random to generate random value from an array of values (in my case pokemon names, and i am generating random pokemon that have been added into this array.) This is linked to a button, and every time the button is pressed the function runs and a new pokemon name is generated.
However, I am struggling to make it so that the function generates a completely different name each time, and sometimes i click the button more than once and it just shows the same pokemon, and i feel it is not a good look as it feels broken sometimes. I was wondering if someone can look at my code and help me out.
var pokemonNames = ["charmander", "squirtle", "bulbasaur"];
function generateRandomPoke() {
var randPoke = pokemonNames[Math.floor(Math.random() * (pokemonNames.length))];
return randPoke;
}
$("#randomizebutton").click( function() {
$("#pokemonname").html(generateRandomPoke);
});
Get the index and remove the element from the array:
function generateRandomPoke() {
var index = Math.floor(Math.random() * (pokemonNames.length))
var randPoke = pokemonNames[index];
pokemonNames.splice(index, 1);
return randPoke;
}
As #jabaa said, you could remove the elements of the array, or another alternative is to have a global variable that stores the names already returned. In case the value to be returned is on the global variable, a new name must be selected.
You have to generate the index until its not match the previous one see the following code
var index = 0;
do{
Index = Math.floor(Math.random() * (pokemonNames.length));
} while(previous_index==index)
previous_index = index;
var randPoke = pokemonNames[index];
return randPoke;
}
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 4 years ago.
I have a function that grabs some values from JSON files, and creates an item object.
var searchIndex = [];
function getSearchTerms(param){
var filePath = 'json/' + param + '.json';
$.getJSON(filePath, function( data ) {
var item = {
param: param,
title: data.title,
person: data.fname + ' ' + data.lname
};
// console.log(item);
// searchIndex.push(item);
return item;
});
}
I can see the item objects with correct properties being created when I check the console.
However, when I try to add the objects to searchIndex array, either within the function or within the loop that calls the getSearchTerms function, I get an array with the correct number of rows, but all the values are undefined.
var contentFiles = [ 'a', 'b', 'c'];
for (var i = 0; i < contentFiles.length; i++) {
searchIndex.push( getSearchTerms(contentFiles[i]) );
}
What stupid thing am I doing wrong here? Thank you in advance for your help.
Remember, reading files from the disk takes a little bit of time. Not a lot, but enough to mess with this little bit of code you're trying to write. Now's a good time for you to learn how to use asynchronous code. Here are some slight alterations to specific lines of code that might be able to help.
async function getSearchTerms(param)
await var item
within your loop...
await getSearchTerms(contentFiles[i])
searchIndex.push(object))
I'm no expert, and this is the first SO question I've ever answered. You might need a .next in there or something. If this doesn't work, look further into the concept of async/await functions. In your code, you're pushing objects that haven't gotten their actual value yet, because reading from disk takes a moment. JS travels line by line without waiting, and sometimes you'll have values that need a second to be sorted out.
Why would a for loop terminate early in JavaScript? For some reason my outer for loop terminates after the first repetition. I am new to JavaScript but would expect something like this to work in Java.
function check(){
var elements = document.getElementById('fields').children;
var filteredMolecules = molecules;
console.log(elements.length);
for (i = 0; i < elements.length; i++) {
console.log(elements[i].id)
filterMolecules(filteredMolecules, elements[i].id, 0, 10);
}
}
function filterMolecules(molecules, parameter, lower, upper){
console.log('filtering');
var filteredMolecules = [];
for (i=0;i<molecules.length;i++){
var value = molecules[i].val()[parameter];
filteredMolecules.push(molecules[i]);
}
molecules=filteredMolecules;
}
In check(), I loop through elements which contains 22 items as shown by the first console.log(elements.length). If I remove the method filterMolecules(...) then all 22 IDs are logged. However, with the code as is, only the first id is logged.
I believe the filterMolecules method which should run elements.length number of times is causing the outer for loop to not work. Could someone please explain why this is happening. If relevant, in filterMolecules(...) the data is retrieved from Google Firebase with molecules[i].val()[parameter]. Additionally, both methods use the global variable molecules (line 3 and line 14)
When you don't declare variables in javascript you end up using globals (which can be a difficult to spot source of bugs). So here you are using the same global variable i for both loops. When you start looping thought molecules you are accidentally incrementing the counter loop of your first for. Use different variables or define them with :
for (let i=0;i<molecules.length;i++)
Which will give each loop its own version of i.
In this case, since the declarations are inside individual functions, you could use var too:
for (var i=0;i<molecules.length;i++) {
// etc.
}
I am currently building a small web application with similar functionality across all modules. I want to code small generic functions so that all programmers next to me, call these functions and these functions return necessary but important data for them to implement their functionality. In this example, I am trying to deal with the typical "choose true or false" exercise. So from the template.php they call this function:
function checkAnswers(){
var radiobuttons = document.form1.exer1;
var correctAnswers = answers(); //this is an array of string
var checkedAnswers = checkExerciseRB(radiobuttons, 2, correctAnswers);
for(i=0; i<checkedAnswers.length; i++){
alert(checkedAnswers[i]);
}
}
Function checkExerciseRB is my generic function, it is called from checkAnswers.
function checkExerciseRB(rbuttons, opciones, correct){
var answers = new Array();
var control = 0;
for(i=0; i<rbuttons.length; i++){
var noPick="true";
for(j=0; j<opciones; j++){
if(rbuttons[control+j].checked){
if(rbuttons[control+j].value==correct[i]){
answers[i]= 1;
noPick="false";
break;
}
else{
answers[i]=2;
noPick="false";
break;
}
}
}
if(noPick=="true")
answers[i]=0;
control=control+opciones;
}
return answers;
}
It works great but while looking at my favorite browsers (FireFox, Chrome) error log it says:
TypeError: rbuttons[control + j] is undefined
Any clue on how to deal with this matter?
This probably means that control + j is greater than or equal to the length of the array rbuttons. There's no such array element as rbuttons[control + j].
You should learn how to use the JavaScript debugger in your favorite browsers! Debuggers are great. They let you watch this code run, line by line, as fast or as slow as you want, and watch how the value of control changes as you go.
You’ll watch it, and you’ll think “Oh! That line of code is wrong!”
You're looping through rbuttons.length times, but in each loop you're adding 2 to control. Using control to index your array, you're going to run past the end.
Does the index specified by control + j exist in the array? i.e: If that evaluates to 4, is there at least 5 items in the array?
Also, you should be using var i, var j, etc inside your for loop. Without it your variables are leaking into the scope this code is executed in (most likely the global scope, and that's not good) :)
There are two JSON var (JSON.parse'd already)
var acc http://pastebin.com/7DyfFzTx
var sit http://pastebin.com/vnZiVaDx
My objective is to loop each var to compare acc.items.site_name with sit.items.main_site.name to see if they are equal. If they are equal, then I need to store sit.items.main_site.site_url in a variable. I am using this code:
for(i=0; i < acc.items.length;i++)
{
aname = acc.items[i].site_name;
for(i=0; i < sit.items.length;i++)
{
sname = sit.items[i].main_site.name;
if (aname == sname)
alert("same "+aname);
}
}
But the alert only logs "same Physics" . "Physics" is the first object in acc.items. This means that the loop is only comparing first acc.items but how do I make it compare the second and further on objects (e.g. "TeX - LaTeX")
Well, you can start by using a different control variable for the outer and inner loops. :)
Also, notice how you are running through every element of the second object each time you consider an element in the first object- that doesn't seem like what you want to do.
Try reviewing this posting for a more modular method:
http://jsperf.com/recursive-vs-json-object-comparison
You're using the same variable for the inner and outer loop.
Give the second for loop a different variable name than i