while statement
while ( condition )
{
statements
}
The while statement evaluates the condition, and if the condition evaluates to true, executes the statements enclosed within brackets. When the condition evaluates to false, it transfers control to the statement following the while statement.
Validating Sums in a 3D Grid Using a while Loop
In the following example, the while statement is used in the validation code of a 3D grid to check that the sum for each row is 24:
While statement in use
The 3D grid contains 3 numeric list questions: q2 (sleep), q3 (work) and q4 (leisure).
The following code is entered in the validation code field to evaluate the respondent's answers:
JScript example:
var codes = f("q2").domainValues(); //array with all codes
var i : int = 0;
var correctSum : Boolean = true; //Boolean variable. Will be set to false when a sum is not correct
while(i<codes.length && correctSum)
{
var code = codes[i]; //current code
//calculate the sum for one row:
var sum : int = f("q2")[code].toNumber()+f("q3")[code].toNumber()+f("q4")[code].toNumber();
if(sum != 24)
{
correctSum = false;
}
i++;
}
if(!correctSum)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Currently the sum for "+f("q2")[code].label()+" is "+sum+".");
}
JavaScript example:
var codes = f("q2").domainValues(); //array with all codes
var i = 0;
var correctSum = true; //Boolean variable. Will be set to false when a sum is not correct
while (i < codes.length && correctSum) {
var code = codes[i]; //current code
//calculate the sum for one row:
var sum = f("q2").item(code).toNumber() + f("q3").item(code).toNumber() + f("q4").item(code).toNumber();
if (sum != 24) {
correctSum = false;
}
i++;
}
if (!correctSum) {
RaiseError();
SetQuestionErrorMessage(LangIDs.en, "Please make sure that the total number of hours for each day equals 24. Currently the sum for " + f("q2").item(code).label() + " is " + sum + ".");
}
Let us see what actually happens here when the question is answered as in the picture above.
The first statement,
var codes = f("q2").domainValues();declares an array called codes, which contains all the codes from the answer list of q2. (Since q2, q3 and q4 are all inside the same 3D grid, they share the same answer list, so we could have used any of these questions to populate the array.) We have used the default codes ("1","2","3","4","5","6","7") in this answer list, so the first statement declares an array of length 7 where the items have the following values:
codes[0] = "1"
codes[1] = "2"
codes[2] = "3"
codes[3] = "4"
codes[4] = "5"
codes[5] = "6"
codes[6] = "7"
The main advantage with using domainValues here is that we can do any changes we like to this answer list (including adding/removing items and changing the codes) without having to change the script.
Then in the next statement we declare a new variable i as an integer with the initial value 0 (zero). This will be used to index the array. After that the Boolean correctSum is declared with the initial value true. Note how in the JScript.NET version of the code we declare Types for i and correctSum variables whereas in the JavaScript code the type is not declared.
JScript example:
var i : int = 0; var correctSum : Boolean = true;
JavaScript example:
var i = 0; var correctSum = true;
So, when we enter the while loop the first time, the condition will evaluate to true because i is 0, which is less than the length of the array codes (which is 7), and correctSum is true.
while(i<codes.length && correctSum)With i equal to 0, the first statement
var code = codes[i];will set code to the first code, "1".
A variable sum is then set in the statement
JScript example:
var sum : int = f("q2")[code].toNumber()+f("q3")[code].toNumber()+f("q4")[code].toNumber();
JavaScript example:
var sum = f("q2").item(code).toNumber()+f("q3").item(code).toNumber()+f("q4").item(code.toNumber();
to 7+8+9, which is 24. (This is the numbers entered for Monday in the 3D grid, see screenshot above.)
The condition in the following if statement will evaluate to false, so the statement inside the curly brackets will not be executed. Consequently, correctSum will remain true:
if(sum != 24)
{
correctSum = false;
}
At the end of the loop i is increased by 1:
i++;That completes the first iteration of the while statement. The next time the condition in the while statement is evaluated, i is 1. 1 is less than 7, and correctSum is still true, so the condition still evaluates to true, and the statements are to be run a second time.
while(i<codes.length && correctSum)With i equal to 1, the first statement
var code = codes[i];will set code to the second code, "2".
The sum will now be 7+11+6 (Tuesday), which is 24.
JScript example:
var sum : int = f("q2")[code].toNumber()+f("q3")[code].toNumber()+f("q4")[code].toNumber();
JavaScript example:
var sum = f("q2").item(code).toNumber()+f("q3").item(code).toNumber()+f("q4").item(code).toNumber();
The condition in the if statement will again evaluate to false (so correctSum will not be changed), and i is increased by 1 at the end of the loop:
if(sum != 24)
{
correctSum = false;
}
i++;
The third time the condition in the while statement is evaluated, i is 2. 2 is less than 7, and correctSum is true. The condition evaluates to true, and the statements are to be run a third time.
while(i<codes.length && correctSum)i is now 2, and code will be set to the third code, "3":
var code = codes[i];The sum will now be 8+9+8 (Wednesday), which is 25.
JScript example:
var sum : int = f("q2")[code].toNumber()+f("q3")[code].toNumber()+f("q4")[code].toNumber();
JavaScript example:
var sum = f("q2").item(code).toNumber()+f("q3").item(code).toNumber()+f("q4").item(code).toNumber();
Now the condition in the if statement will yield true because 25 is not equal to 24. So this time correctSum will be set to false.
if(sum != 24)
{
correctSum = false;
}
Then at the end i is increased to 3.
i++;The fourth time the condition in the while statement is evaluated, the first part will be true because 3 is less than 7, but correctSum is now false, so the entire expression will give false. So this time the execution of the loop will end.
while(i<codes.length && correctSum)Since correctSum is now false, the condition in the following if statement will evaluate to true and the two statements inside will be executed. The statements are calling functions to identify this as an error situation, and to display an error message. We will get back to functions later, also showing a function that would make it easier to calculate the sum.
JScript example:
if(!correctSum)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Currently the sum for "+f("q2")[code].label()+" is "+sum+".");
}
JavaScript example:
if(!correctSum)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Currently the sum for "+f("q2").item(code).label()+" is "+sum+".");
}
do while statement
The do while statement is similar to the while statement. The only difference is that the looping condition is checked at the end of the loop, instead of at the beginning. This means that the enclosed statements are executed at least once. The condition is not evaluated before after the statements are executed the first time.
do
{
statements
}
while (condition);
This may be used for example when the condition uses variables that aren't introduced before inside the loop. In the example below the variable correctSum is not introduced before inside the loop.
Validating Sums in a 3D Grid Using a do while Loop
Using the same example as in Validating Sums in a 3D Grid Using a while Loop, this code would give the same validation of the 3D grid:
JScript example:
var codes = f("q2a").domainValues(); //array with all codes
var i : int = 0;
do
{
var code = codes[i]; //current code
//calculate the sum for one row:
var sum : int = f("q2a")[code].toNumber()+f("q3a")[code].toNumber()+f("q4a")[code].toNumber();
var correctSum : Boolean = (sum == 24); //false if sum not 24, true if sum is 24
i++;
}
while(i<codes.length && correctSum)
if(!correctSum)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Currently the sum for "+f("q2a")[code].label()+" is "+sum+".");
}
JavaScript example:
var codes = f("q2a").domainValues(); //array with all codes
var i = 0;
do {
var code = codes[i]; //current code
//calculate the sum for one row:
var sum = f("q2a").item(code).toNumber() + f("q3a").item(code).toNumber() + f("q4a").item(code).toNumber();
var correctSum = (sum == 24); //false if sum not 24, true if sum is 24
i++;
}
while (i < codes.length && correctSum)
if (!correctSum) {
RaiseError();
SetQuestionErrorMessage(LangIDs.en, "Please make sure that the total number of hours for each day equals 24. Currently the sum for " + f("q2a").item(code).label() + " is " + sum + ".");
}
for Statement
The for statement is similar to the while statement in that it repeatedly executes a set of statements while a condition is true. The difference is that the syntax of the for loop includes both an initialization statement and an update statement in its syntax:
for (initializationStatement; condition; updateStatement)
{
statements
}
The initialization statement is executed at the beginning of the loop execution. Then the condition is tested, and if it is true, the statements enclosed within brackets are executed. If the condition is false, the loop is terminated and the statement following the for statement is executed. If the statements enclosed within the brackets of the for statement are executed, the update statement is also executed, and then the condition is reevaluated. So the enclosed statements and the update statement are repeatedly executed until the condition becomes false.
Typically the initialization statement declares an integer with an initial value, and the update statement increases or decreases this integer. The condition then usually defines the limit.
Validating Sums in a 3D Grid Using a for Loop
In the previous examples, we stopped validating sums as soon as we found the first error. Here is an example using the for statement where we validate all rows and provide an error message listing all rows with errors.
Figure 2 - for statement in use
The 3D grid contains 3 numeric list questions: q2 (sleep), q3 (work) and q4 (leisure). In addition we have a hidden multi question error_rows which is used to set which rows have incorrect sums.
The following code is entered in the validation code field to evaluate the respondent's answers:
JScript example:
var precodes = f("q2").domainValues(); //array with all precodes
var error_rows = new Array();
for(var i = 0;i<precodes.length;i++)
{
var code = precodes[i]; //current precode
// calculate the sum for the row:
var sum : int = f("q2")[code].toNumber()+f("q3")[code].toNumber()+f("q4")[code].toNumber();
if (sum != 24) {
error_rows.push(precodes[i]);
}
}
if(error_rows.length>0)
{
f("error_rows").set(error_rows);
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Please correct for the following rows: "+f("error_rows").categoryLabels()+".");
}
JavaScript example:
var precodes = f("q2").domainValues(); //array with all precodes
var error_rows = new Array();
for (var i = 0; i < precodes.length; i++) {
var code = precodes[i]; //current precode
// calculate the sum for the row:
var sum = f("q2").item(code).toNumber() + f("q3").item(code).toNumber() + f("q4").item(code).toNumber();
if (sum != 24) {
error_rows.push(precodes[i]);
}
}
if (error_rows.length > 0) {
f("error_rows").set(error_rows);
RaiseError();
SetQuestionErrorMessage(LangIDs.en, "Please make sure that the total number of hours for each day equals 24. Please correct for the following rows: " + f("error_rows").categoryLabels() + ".");
}
(The push method is used to add elements to an array (go to Removing and Adding Elements for more information)).
Loop Nodes
You can build loops in Forsta Plus if you want to ask the same question(s) for different elements. The loops can be nested. For example, we can have a loop that iterates through some TV channels asking a question for each TV channel about what kind of programs the respondent watches on the different channels. Then, for those program types there is another loop inside the first one in which they are asked to rate those programs for each channel.
The inner loop is called programs and is placed inside the loop called channels. In the inner loop we have a single question called rating in which we want to ask the respondent about the program types he/she has specified in types (the multi question in the outer loop). So the programs loop uses the same list of program types as the types question, and the loop is filtered with a code mask based on the types question, f("types"). So the loop will only iterate through the program types answered for a specific channel.
Figure 3 - Loop Nodes example
In scripts inside the loops you can refer to the questions as usual, e.g. f("types"). This will refer to that question in the current iteration of the loop. However, if you need to refer to questions in the loops from scripts outside of the loops, you have to specify which iteration you refer to.
For example, f("types","1") refers to the question called types in the iteration with code 1 within this loop. With nested loops (like we have here), you specify the innermost iteration first, so f("rating","3","1") refers to the rating question from the iteration with code "3" of the inner loop programs. For the outer loop channels it is the iteration with code "1".
break statement
break;
Sometimes you want to terminate the execution of the loop before it is finished (before the condition in the loop statement evaluates to false). Then you may use the break statement. The break statement will terminate execution of the loop and transfer control to the statement following the loop.
Validating Sums in a 3D Grid Using a for Loop and break
The example we used for the while loop can be programmed using a for loop and a break like this:
JScript example:
var codes = f("q2b").domainValues(); //array with all codes
for(var i : int = 0; i<codes.length; i++)
{
var code = codes[i]; //current code
// calculate the sum for one row:
var sum : int = f("q2b")[code].toNumber()+f("q3b")[code].toNumber()+f("q4b")[code].toNumber();
if(sum != 24)
{
RaiseError();
SetQuestionErrorMessage(LangIDs.en,"Please make sure that the total number of hours for each day equals 24. Currently the sum for "+f("q2b")[code].label()+" is "+sum+".");
break; //terminate execution of the loop when an error is found
}
}
JavaScript example:
var codes = f("q2b").domainValues(); //array with all codes
for (var i = 0; i < codes.length; i++) {
var code = codes[i]; //current code
// calculate the sum for one row:
var sum = f("q2b").item(code).toNumber() + f("q3b").item(code).toNumber() + f("q4b").item(code).toNumber();
if (sum != 24) {
RaiseError();
SetQuestionErrorMessage(LangIDs.en, "Please make sure that the total number of hours for each day equals 24. Currently the sum for " + f("q2b").item(code).label() + " is " + sum + ".");
break; //terminate execution of the loop when an error is found
}
}
When a row for which the sum is not equal to 24 is found, the loop will terminate without going through the last iterations.
continue Statement
continue;
The continue statement is similar to the break statement, but instead of transferring control to the statement after the loop it terminates the execution of the current iteration and skips to the next iteration of the loop (after checking the loop condition).
Calculating Averages in a Grid
We have a grid question course, and for reporting we want to calculate an average of the answers to the grid and store it in a hidden numeric question called average. The grid uses a 5 points scale (and 6 as a value for "Don't know"). Obviously, we want the average to be calculated based on the questions with answers 1-5. The “Don’t know”s (6) should not be included in the calculation.
Figure 4 - Calculating averages in a grid
JScript example:
var codes = f("course").domainValues(); //all codes of the rows in the grid
var sum : int = 0; //this will hold the sum of the scores
var count : int = 0; //this will hold the number of items
for(var i : int = 0;i<codes.length;i++) //iterate through the rows in the grid
{
var code = codes[i]; //current code
if(f("course")[code].get() == "6") //don't know
{
continue; //skip directly to next iteration
}
//here we know that the answer isn't don't know
sum += f("course")[code].toNumber(); //add current score to sum
count++; //increase counter with one
}
if(count>0) //prevent division with zero
{
f("average").set(sum/count); //calculate average
}
else
{
f("average").set(null); //set null value if answered "don't know" on all
}
JavaScript example:
var codes = f("course").domainValues(); //all codes of the rows in the grid
var sum = 0; //this will hold the sum of the scores
var count = 0; //this will hold the number of items
for (var i = 0; i < codes.length; i++) //iterate through the rows in the grid
{
var code = codes[i]; //current code
if (f("course").item(code).get() == "6") //don't know
{
continue; //skip directly to next iteration
}
//here we know that the answer isn't don't know
sum += f("course").item(code).toNumber(); //add current score to sum
count++; //increase counter with one
}
if (count > 0) //prevent division with zero
{
f("average").set(sum / count); //calculate average
}
else
{
f("average").set(null); //set null value if answered "don't know" on all
}
If the respondent has answered "don’t know" (code 6) on one of the questions, the last two assignment statements are skipped because of the continue, so sum and count will not be updated for "Don't know" answers.
label Statement Nested Loops
Sometimes you need to specify exactly where execution is to continue after a break or a continue is used. Especially when you have nested loops (loops in loops). To specify which loop you want the break or continue to apply to, you can specify a label and refer to that in your break or continue statement. The names of labels follow the same rules as variable names (go to Naming for more information).
Specifying a label:
label:Referring to a label:
break label;continue label;
Calculating Averages on a Single Question in a Loop
Referring back to the example with two nested loops in Forsta Plus, One iterating through some TV channels, and the other iterating through some types of programs:
Figure 5 - Calculating averages on a single question loop
For each channel, the respondent answer what type of programs they watch on that channel, and then give a rating for each program type for that channel in the rating question. Now we want to write a script that calculates an average of these ratings for each channel. Here is the list of iterations in the channels question:
Figure 6 - Iterations in the channels question
We set up a hidden numeric list question avg with the same list:
Figure 7 - hidden numeric list question avg
Now, after the loop we insert a script node to calculate these averages.
Figure 8 - Inserting a script node to calculate averages
This script must run through all the items in the channels loop, and for each of them run through all the ratings given in the programs loop:
JScript example:
var ccodes = f("channels").domainValues(); //all codes of the channels loop
Outer:
for(var i : int = 0;i<ccodes.length;i++) //iterate through channels
{
var ccode = ccodes[i]; //code of current channel
//initialize the counter and sum for this channel:
var sum : int = 0, count : int = 0;
//the codes of the program types for this channel:
var pcodes = f("types",ccode).categories();
Inner:
for(var j : int = 0;j<pcodes.length;j++) //run through the codes in the programs loop
{
var pcode = pcodes[j]; //code of current program type
//check rating for this program type for this channel:
if(f("rating",pcode,ccode).get() == "6")
{
continue Inner; //go to next program type
}
sum += f("rating",pcode,ccode).toNumber(); //add score to sum
count++; //increase counter with one
}
//set average score for this channel:
if(count>0)
{
f("avg")[ccode].set(sum/count);
}
else
{
f("avg")[ccode].set(null);
}
}
JavaScript example:
var ccodes = f("channels").domainValues(); //all codes of the channels loop
Outer:
for (var i = 0; i < ccodes.length; i++) //iterate through channels
{
var ccode = ccodes[i]; //code of current channel
//initialize the counter and sum for this channel:
var sum = 0, count = 0;
//the codes of the program types for this channel:
var pcodes = f("types", ccode).categories();
Inner:
for (var j = 0; j < pcodes.length; j++) //run through the codes in the programs loop
{
var pcode = pcodes[j]; //code of current program type
//check rating for this program type for this channel:
if (f("rating", pcode, ccode).get() == "6")
{
continue Inner; //go to next program type
}
sum += f("rating", pcode, ccode).toNumber(); //add score to sum
count++; //increase counter with one
}
//set average score for this channel:
if (count > 0)
{
f("avg").item(ccode).set(sum / count);
}
else
{
f("avg").item(ccode).set(null);
}
}
This example uses nested loops (loops in loops), i.e. one outer loop that iterates through the channels loop and one inner loop that iterates through the answers in the programs loop. We have used the labels Inner and Outer in the script to make sure that it is the iteration of the inner loop that is terminated with the continue, not the iteration of the outer loop. (We do not actually refer to the label Outer anywhere in the script, but we have included the label for clarity.)
Write a script that presets a grid question q2, which has a scale from 1 to 5, so that the first time the respondent comes to that question, all rows in the grid have been set to the middle value 3. Make sure that the values are not reset if the respondent reopens the questionnaire or uses the back button.
The answer is given in APPENDIX A Answers to Exercises.