JavaScript: unable to add string from API in json - javascript

When I am adding values normally it's getting added to json:
Code:
for (var k = 0; k < result.length; k++) {
result[k]["sender"] = k;
}
But when I call value from api and try to store it in result[k]["sender"] then it's not saving in json and values are getting printed on console:
for (var k = 0; k < result.length; k++) {
var url = "//api";
$.ajax({
url: "https://reverse.geocoder.api.here.com/6.2/reversegeocode.json?prox="+result[k]['lat'].toString()+","+result[k]['lon']+"&mode=retrieveAddresses&app_id=***&app_code=***",
context: document.body,
success: function (data) {
console.log(k, typeof data['Response']['View'][0]['Result'][0]['Location']['Address']['Label']);
result[k]["sender"] = data['Response']['View'][0]['Result'][0]['Location']['Address']['Label'];
}
});
}
Error:
Can anyone guide me what I'm missing?

Change var k to let k.
k is incremented at each iteration of the loop, and its final value will be result.length at which point the loop stops.
Your loop first runs for result.length times, incrementing k every time, and you start all those requests with $.ajax. Note, you only start them here, as they are asynchronous! That means, the time when they complete and success is called will be later on, and meanwhile the rest of the code will continue running.
So, later on, the requests complete, and they attempt to access result[k]. However, k === result.length at this point (because the loop has long finished running), so result[k] will be undefined.
With let you can make use of a special feature in ES6 where a let variable inside a for statement actually creates two instances of the variable, with the first one actually counting and the second one being reinitialized to the current value of the first and made available to the inner scope (but keeping its value inside the closure). That's a simplification, but the bottom line is that this way each iteration of your loop will have its own k which keeps its value even after the loop advanced.
Note: Your url seems static at the moment, but if it depends on k at some point then it should also be let, otherwise you get the same problem there as well.
An alternative way, for example if you can't use ES6, would be to create a new (function) scope using an IIFE (immediately-executed function expression) into which you pass the variable as argument:
for (var k = 0; k < result.length; k++) {
(function (k) {
// Do $.ajax and everything else here
})(k);
}
Or, you could use forEach (which takes a callback so it will automatically have a new variable every time).

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

var i = [0] in a for loop, then incremented i++ - why does it work?

In a book that I'm reading (JavaScript & JQuery - Interactive Front End Development by Jon Duckett) there's an interesting error or (at least I think so) which doesn't stop the code from working:
for (var i = [0]; i < options.length; i++) {
addEvent(options[i], 'click', radioChanged);
}
This is a part of script that loops through all radio buttons in a form and attaches an event listener (it doesn't really matter what it does).
But...
Why is i initialised as an array at all?
Why does the incrementation work?
Why does the whole loop work?
Of course if you replace var i = [0] with var i = 0 the code still works.
When you add some alerts to check the value of i in each iteration of the loop and the type of i, at the second iteration type of i changes from object (after all in the first iteration it is an array) to number.
That's a kind of implicit type conversion I have never come across so far (and google don't help much). Can anyone explain what's going on under the hood?
for (var i = [0]; i < options.length; i++) {
addEvent(options[i], 'click', radioChanged);
alert(i); // --> 1 2 3 ...
alert(type of i); // --> object number number ...
}
The spec says (ยง 11.3.1) that the ++ operator converts its operand to a number before incrementing:
Let oldValue be ToNumber(GetValue(lhs)).
When called on an object, the GetValue internal operation will call toString(), which, for an array, will join its elements, returning '0'.

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!

Is it ok to use the same vars on consecutive loops in Javascript?

If you have a loop set up like
for (var i=0, t=myArray.length; i < t; i++)
once that loop is complete, is it ok to use the i and t vars in the next non-nested loop within the function?
I've been numbering my nested loops differently like
for (var i2=0, t2=anotherArray.length; i2 < t2; i2++)
but also doing the same for non-nested loops. I'm wondering if there's a need?
I ask, as Flash doesn't like it when you use the same vars, regardless if the second loop is not nested.
Thanks for your time and help.
Normally loop variables aren't used for anything other than doing calculations within the loop.
Even though the variable is available outside the loop, normally it isn't used.
Sometimes you might set another variable to the exit value of a loop variable.
for(i=0;i<max;i++){
....
if (some exit condition that leaves the loop early){
//should assign value here instead of outside the loop
exitvalue = i;
break;
}
}
//exitvalue = i; //can assign the value of i here (try to avoid this)
Therefore it is usually quite safe to reuse loop variables.
The var i or t is declared in the scope where the loop is in.
Javascript uses "Hoisting".
So,
for (var i=0, t=myArray.length; i < t; i++) // first loop
is equal to:
var i,t;
for (i=0, t=myArray.length; i < t; i++)
So, when you use the same variables next time in a non-nested loops, like this:
for (var i=10, t=myArray.length; i < t; i++) // second loop
the i and t are already hoisted and hence you are just doing something like this:
var i = 0;
i = 10;
So it is okay to use i and t vars in the next non-nested loop.

Categories