For loop skipping iterations - javascript

I have a for loop which will iterate through all choices and and set a value for them.
For some reason, when I run the code it does the first iteration, then skips to the last and does that one 3 times, or so according to the console.
code:
for (i = 0; i < 4; i += 1) {
console.log(i)
var generated = word
while (generated == word) {
generated = wordsJson.characters[Math.floor(Math.random() * wordsJson.characters.length)]
}
choices[i].innerHTML = translate(generated)
}
What I get in console:
0
(3) 3
This is my first time asking something on stackoverflow. If you need more information, please ask.

It appears that the variable i is getting modified outside of the for loop.
Typically you would want to declare your iterator variable (in this case i) so that it's scoped to the loop, which would look like:
for (let i = 0; i < 4; i += 1) { ... }
Note, specifically, the addition of let. Since you haven't done that, it means that either i is already explicitly declared somewhere or, if not, that you've created a new global variable i.
Since you've got code you haven't shown that also seems to relate to i (choices[i]) and methods that we don't know the exact function of (translate()) it's hard to say for certain, but that would be the first place to look.
If not, posting some additional code so we can see the other functionality would be helpful.

Related

Do I have to use the same prepareCall when using addBatch & executeBatch? Why?

I'm new to using JDBC and am learning how to use batch processing. After wasting a lot of time trying to figure out why my combination of JavaScript and SQL stored procs didn't work, I think I learned that you have to use one prepareCall when using addBatch and executeBatch. Is this true? If so, why?
To exemplify, here's some example input:
var vals = [["value1_1","value2_1","value3","value4","value5","value6_1"],
["value1_2","value2_2","value3","value4","value5","value6_2"]]
The below loop works as expected. Note that I prepareCall before entering the loop.
params = Array(vals.length).fill("?")
pstmt = conn.prepareCall("{call "+storedProcedure+"("+params+")}");
for (var i = 0; i < vals.length; i++) { // for each array
for (var j = 0; j < vals[i].length; j++) { // for each value within each array
// set the string
pstmt.setString(j+1, vals[i][j]);
}
pstmt.addBatch();
}
try {
pstmt.executeBatch();
} catch (err) {
//my err msg code
pstmt.close();
conn.close();
}
Now, sometimes I have records that have a different number of parameters so I thought that I could just move prepareCall into the first loop so that I can change the number of parameters as needed for each input array.
for (var i = 0; i < vals.length; i++) { // for each array
// moved prepareCall here
params = Array(vals.length).fill("?")
pstmt = conn.prepareCall("{call "+storedProcedure+"("+params+")}");
for (var j = 0; j < vals[i].length; j++) { // for each value within each array
// set the string
pstmt.setString(j+1, vals[i][j]);
}
pstmt.addBatch();
}
try {
pstmt.executeBatch();
} catch (err) {
//my err msg code
pstmt.close();
conn.close();
}
For this second loop, I don't get any errors from Javascript but I get a foreign constraint error from my stored proc. My stored proc makes a set of CALLs to create records based on the value of the last parameter. I understand that the error that I get is telling me that one of the FKs doesn't exist. This would only happen if the wrong set of CALLs was invoked or the right set of CALLs was invoked but one of the CALLs failed. I'm sure neither is the issue since the first loop works as expected.
Thus, I'm interested to learn why I have to use one prepareCall when I executeBatch? To confirm, I have to use totally separate prepareCalls when I have call different stored procedures with different numbers of parameters?
I don't think it matters but for good measure: I'm using MySQL 5.7.
The method prepareCall returns a CallableStatement object that is compiled for the specific statement that you passed to prepareCall. When you call addBatch, the set of parameters is added to that specific instance of CallableStatement. If you execute prepareCall again, you get a different CallableStatement handle, with its own batch.
So, in your second piece of code in each iteration of the outer loop, you create a new CallableStatement, losing the previous callable statement and its batch (creating a memory leak as well in your code and possibly in your DBMS). As a result, when you call executeBatch, you will only execute the last statement you prepared, with only a single set of parameter values.
If you need to execute distinct statement texts, you will need to prepare and execute them in order, and you cannot use batch(*).
* - You could use batch if multiple sets of parameters use the same statement text, but if there are order dependencies between different statement texts, this might get tricky

How strict is the syntax of a for-loop

So I have a fairly good amount of experience in coding. I've dabbled in Basic, HTML, Javascript, C, and C++, though the ones I've been using most recently are HTML and Javascript.
I am incredibly familiar with the for-loop. I've used it many times to loop through arrays, to operate recursive functions, etc. I know what it does and how to use it, but my question is about how it works.
Premise
In most languages, the basic syntax of a for loop is such:
var upperLimit = 10;
for(var i = 0; i < upperLimit; i++) {
/*Code to be executed*/
console.log(i);
}
In Javascript, this will output the numbers from 0 to 9 in the console.
I know that the parentheses contains 3 parts, each separated by semicolons.
The first is the initialization, which typically sets up the variables to be used to loop the statements.
The second is the condition, which runs before any of the code between the curly braces is executed. If it results in a True, the code is executed. Otherwise, the for-loop stops.
The third is the increment, which contains the last bit of code to be executed in the loop, and, by extension, the last thing executed before the next condition check.
Question
So, again, my question is how strict are these definitions?
The initialization's definition doesn't allow for much. It just says that that line is executed once, it's executed before anything else in the loop, and it's scope is limited to the loop. I can't think of much else you'd want to put in that position other than an iterative variable.
But what about the other two? I've seen codes where the condition is simply a variable, and as long as it's positive (since positive numbers taken as a boolean just covert to true), the loop continues.
Then there's the increment, which seems to be the loosest of these parts. Is it really just the last thing to be executed in a code, or does it explicitly need to iterate the variable declared in the initialization? It seems to be the former for the languages I'm familiar with.
For example, I decided to make a non-standard for-loop, and I came up with this routine:
var numbers = [0,1,2,3,4,5,6,7,8,9];
for(var i = 0;
numbers.length;
console.log(numbers.pop())) {}
It runs exactly as I expected: It outputs each member of the numbers array in the console in descending order, leaving an empty numbers array afterwards, and it's done using what is basically an empty for-loop.
Ending
So are my assumptions correct? If so, are there any practical applications for using a for-loop in a format apart from the one I wrote at the top of this question (possibly closer to he second format)?
Before all, you give a array
var numbers = [0,1,2,3,4,5,6,7,8,9];
The codes below is a correct for loop.
for(var i = 0;
numbers.length;
console.log(numbers.pop())) {}
Javascript defined for like this
for ([initialization]; [condition]; [final-expression])
statement
For you code initialization is 'var i = 0', and execute once at start of loop.
The condition is 'numbers.length', and value is 10. When a number not 0, Javascript will convert it to boolean true. So condition is true.
The final-expression is 'console.log(numbers.pop())'. When execute 'numbers.pop()', numbers.length change to 9. But it still is true.
At second time, condition will return true again. The final-expression is execute too.
Until numbers.length become 0, Javascript convert it to boolean false. The loop will end.
The scope of the initialized variable is not limited to the loop, it's valid for the whole function (undefined before that line). You can initialize multiple variables using a comma. for (var i=0, j=1; i < 10; i++)
The second part, anything that evaluates to a truthy value will cause the loop to keep going:
Truthy: 1, -1, true, "something", {a: 2}
Falsey: 0, false, null, undefined
You could omit this and rely on a break in your code
The third part just lets you update the looping variable, you could omit it and do it within the for loop body.
Here's an answer that provides a nice way to loop that is non-standard, and comes with caveats, please see the link.
var list = [{a:1,b:2}, {a:3,b:5}, {a:8,b:2}, {a:4,b:1}, {a:0,b:8}];
for (var i=0, item; item = list[i]; i++) {
// Look no need to do list[i] in the body of the loop
console.log("Looping: index ", i, "item" + item);
}
In most languages, the basic syntax of a for loop is such:
for(initialization; condition; iteration) {
/*Code to be executed*/
}
Both three are usual expressions and you can use any valid expressions here:
for(
var i=arr.length, othercond=true;
i;
othercond?i--:i++, console.log(i),updateothercond()
);

Weird looking Javascript for loop

I have never seen a JavaScript loop such as this for( ; i-- ; ), used in the code:
uid: function (len) {
var str = '';
var src = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var src_len = src.length;
var i = len;
for (; i--;) {
str += src.charAt(this.ran_no(0, src_len - 1));
}
return str;
}
I understand the behavior, but I would like it if somebody could share some insights about this type of for loop.
This is a syntax of the for-loop construction:
for ([initialization]; [condition]; [final-expression])
statement
In your case for (; i--;) {:
no variables are initialized, because var i = len; inintialized earlier, so it's not needed.
condition part will be truthy until i becomes 0 then loop will terminate. i-- is executed on before each iteration, and due to -- operator it will eventually become 0, so it's falsy, and loop will stop.
since i is decremented in condition part of the loop, final-expression is not needed too. Another way to put it: since i is not used inside the loop, it does not matter whether we decrement it before each loop iteration or after each loop iteration.
That being said, it's better to avoid writing loops like above, as it's pretty confusing and hard to read. Prefer traditional for-loops notation.
From MDN - for - Optional for expressions:
All three expressions in the head of the for loop are optional.
You don't have to specify all three expressions in for loops. For example, for (;;) is a common wa of writing infinite loop.
In your case, while(i--) would have done the same, there is no good reason to write for (; i--;).
I'd also note that for(var i=len;i>=0;i--) is more robust - it protects you from the case len is negative.
This could be rewritten to
uid: function (len) {
var str = '';
var src = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var src_len = src.length;
var i = len;
while (i >= 0) {
str += src.charAt(this.ran_no(0, src_len - 1));
i = i - 1;
}
return str;
}
The for statement creates a loop that consists of three optional
expressions.
Javascript consider 0 == false that's why in the case you presented the loop will run until the i variable became zero. It will loop as many times as the src string size.
Note: i-- uses the variable value then decrements it. Take a look at the following situation:
for(;i--;) { // i = length in the condition
// i === length - 1 here. It will not overflow the array
}
for(;i--;) { // i = 1
// i === 0 here. It will be the last loop
}
for(;i--;) { // i == 0 == false
// Not executed
}
There is nothing wrong.
for(`INIT`;`TEST`;`ACTION`)
{
`WORK`;
}
The INIT (initialization) can be done outside the loop.
var i=0;
for(;i<=100;i++)
//do something
The TEST part yield a result that is either true or false. Now in this case value of i is tested. Until it becomes zero this works.
The ACTION part is generally used to change the loop variable. But you can leave it also or probably add it to the TEST section like it is done here.
Look this examples are going to clear your idea
var i=0;
for( i++; i++; i+=j); //No body
var i=0;
for(;;); //an infinite loop
var i;
for(i=-4;i;i++);//
Even sometimes WORK is placed in ACTION.
Example:
factorial of x
for(var i=1,ans=1;i<=x;ans=ans*(i++));
Which can be written this way also-
var ans=1;
for(var i=1;i<=x;i++)
ans=ans*i;
NOTE: You can write whichever way you want. It doesn't matter as long as you have written the code properly. Get used to this kind of form you will see them a lot.
Though, it is better to write sometimes in compact form , but remember you should keep the readability.
That's just for loop. Before the first semicolon the variable used in the loop is usually declared. But in this case the variable, the variable used in the loop is declared earlier:
var i = len;
^^^
for (; i--;) {...
So there is no need to redeclare.
After the first semicolon, there is a condition for the loop to run (i.e. i<6, i>3, etc). If condition returns false, or 0, the loop is exited. In this case, the loop will be broken out when i is 0. It happens eventually, because -- after i decrements it, and therefore there is no need for the expression, which is the place after the second semicolon is reserved for.
The first parameter of the for loop has already been defined, so the initial comma is there to delimit it's place in the parameter list.

Iterator scope not local?

I was just writing two JavaScript functions, one of which took in a long string, looped over it until it hit a space, then called the other function to print the input before the space into the DOM. The first function would then continue on with input after the space, hit a space, call the print function, etc.
In the process, I kept hitting infinite loops, but only if the string contained a space. I couldn't figure out why, since all the looping seemed to be set up properly. I ultimately figured out that my iterator variable was jumping scope out of my second function printMe and back into the first, readAndFeed, and because of the way the functions were set up, it would always come back as a lower number than the terminating value if there was a space involved.
The first function's loop looked like this:
function readAndFeed(content){
var output = "";
var len = content.length;
for(i = 0; i < len; i++)
{
console.log(i+" r and f increment")
if(content[i] == (" "))
{
printMe(output);
output = "";
}
else if(i==len-1){
output += content[i];
printMe(output)
}
else
{
output += content[i]
}
}
}
The second function is printMe(), and it looped over the string, broke it into three bits, looped over each of them separately (not in a nested fashion), and printed them to the DOM. I used similar loops in it, and I also used i as an iterator.
This would loop over strings with no spaces just fine, but if I threw a space in there, the browser would crash. I tried a bunch of different stuff, but ultimately (by logging the iterator values) realized something was up with i. What worked was changing the i in the printMe function to a j.
I'm confused; this doesn't seem like how I understand variable scope. The functions are defined separately, so it seems like the iterators should be local to those functions and not able to jump out of one into the other.
Here's a jsfiddle
Uncomment the "is an example" part at the bottom to crash your browser. Again, changing the i variables to j in the printMe function completely solved this, but whaaa?
When you don't declare a variable, it is implicitly global. Since you've not declared your loop iteration index i, it is global. If you do that in multiple functions, those globals will collide and one function will accidentally modify the other's variable.
The solution is to make SURE your local variables are declared with var as in:
for (var i = 0; i < len; i++) {
In your case, you need to fix both readAndFeed() and printMe() as they both have the same issue and thus they both try to use the global i. When you call one from the other, it trounces the original's use of i. Here's a fixed version of readAndFeed():
function readAndFeed(content) {
var output = "";
var len = content.length;
// add var here before i
for (var i = 0; i < len; i++) {
console.log(i + " r and f increment")
if (content[i] == (" ")) {
printMe(output);
output = "";
} else if (i == len - 1) {
output += content[i];
printMe(output)
} else {
output += content[i]
}
}
}
If you run your Javascript code in strict mode, then trying to use an undeclared variable actually causes an error (rather than implicitly make it a global) so you can't accidentally shoot yourself in the foot like this.
In your example, i is in fact a global variable. Any assignment without var statement to an undeclared variable declares an implicit global.
To make i local, just include var:
for (var i = 0; i < len; i++)

understanding variable scope with FOR vs IF loop

My goal was to create a function for a click event that would count up on each click, and use that counter as an array index in order to increment through an array. Can someone help me understand why the first code block produced the desired result, but the second does not. I have a feeling it has to do with variable scope, but this is my first JavaScript project. Thank you.
var i = 0;
function click(){
if(i < Questions.length-1){
i++;
div.innerHTML = Questions[i].question;
}
}
Does not produce them saves results as this code:
function click(){
for(var i = 0; i < Questions.length-1; i++){
div.innerHTML = Questions[i].question;
}
}
In the first one you have a global i.
This means:
If anything else uses i it will clobber the value used in click, and
After you call click the first time you're using the value of i as set by the last call of click, or whatever modified it outside of click.
In the second one you have an i local only to the click function. It's initialized, used, and discarded when the method completes.
You set the innerHTML to all the questions, and the last one set will be the one you see.
Even though the first one works (at the moment) having a global variable named i is pretty risky, and brittle. You'd be better off encapsulating the question index.
It doesn't have anything to do with the scope.
The first method is correct because, on each click i is incremented once, and the ith question is displayed.
In the second method, on every click you're iterating through all the questions in one go. Hence, in the end only the last question would be printed in the div.
Javascript arrays begin at index 0, so you need to access the array before incrementing your counter. For instance, your first code should look like :
var i = 0;
function click(){
if (i <= Questions.length-1) {
div.innerHTML = Questions[i].question;
i++;
}
}
or even better (more concise) :
var i = 0;
function click(){
if (i <= Questions.length-1) {
div.innerHTML = Questions[i++].question;
}
}
Note the <= instead of = because if your have a 2-sized array, index 1 is good (and the last entry, actually).
Your second code iterates over the whole array at each click (an then displays only the before-last question in array) whereas the first code only increment when the element is clicked on.
Here is an interpretation. Not sure if that is what you're looking for.
The code block has nothing special to do with javascript.
The first block simply increment i once and the second loops through it until array ends.
Here are some excerpts from my Chrome console:
var i=0;
function callMe(){
if (i < arr.length-1){
i++;
console.log(arr[i]);
}
}
arr = ["one", "two", "three", "four"]
function callMe2(){
for (var j=0; j < arr.length; j++){
console.log(arr[j]);
}
}
And then..
callMe()
two
callMe()
three
callMe()
four
callMe()
<blank>
callMe2()
one
two
three
four
Hope this helps!

Categories