Javsacript for loop giving out weird results - javascript

I wrote a simple for loop to perform some DOM manipulation based on the json response. Here's the code I have
onSuccess: function(a) {
var b = a.items.length;
for (i = 0; i < b; i++)
user_id = a.items[i].id;
$('#user_id').checked = true;
selectUserSettings(user_id);
}
},
In one example I was working with, the resultset 'a' had 14 items in them. The for loop, when adding a breakpoint, shows the value of i at 0,1,2,2,3,2,3,4,2,3,4,5... Basically it resets and begins at 2 and goes upto 1 additional index before doing it over again... What am I doing wrong here ?Any help would be greatly appreciated.

Most probably because of this line for (i = 0; i < b; i++) when i is declared without a let or var keyword , it is in global scope.
Change this to
for (let i = 0; i < b; i++)
Also if my understanding is correct you want to use user_id variable to access the element. If it is so then change
user_id = a.items[i].id;
$('#user_id').checked = true;
to
let user_id = a.items[i].id;
$('#'+user_id).checked = true;

Related

Google apps script - Broken for loop

I'm working in Google apps script and seem to have screwed up one of my for loops. I'm sure that I am missing something trivial here, but I can't seem to spot it.
Code Snippet:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
for (var j = 0; j < zw.length; ++j) {
if (zw[j][9] === 'Yes') {
var masterEmail = [];
var firstLetterLastName = [];
var first2Letter = [];
var masterEmail.push(zw[j][22]);
var firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
var first2Letter.push(zw[j][1].charAt(0).toLowerCase() + zw[j][1].charAt(1).toLowerCase());
//The rest of the function follows...
}
}
What's Not Working:
The for loop doesn't increment. When running the code in a debugger, var j stays at a value of 0.0, and the rest of the function only runs based of off the values in the 0 position of zw.
What I need it to do (AKA - How I thought I had written it:)
The ZW variable is holding a 2 dimensional array of cell values from a Google sheet. I'm looping through that, checking the 9th value of each array entry for a string of "Yes" and then running the rest of the function (for each column with a "Yes") if the condition is true.
I thought I had this working before, but recently had to restructure and optimize some things. Now I'm starting to think I may need to rethink things and use a different loop method. Can anyone educate me?
Edit: Here's a bit more context as requested:
function menuItem1() {
var ui = SpreadsheetApp.getUi();
var response = ui.alert('Are you sure you want to send emails?', ui.ButtonSet.YES_NO);
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
var lastRow = sheets[3].getLastRow();
var zw = sheets[3].getRange(2, 1, lastRow - 1, 26).getValues();
if (response === ui.Button.YES) {
for (var j = 0; j < zw.length; j++) {
if (zw[j][9] === 'Yes') {
var firstLetterLastName = [];
firstLetterLastName.push(zw[j][1].charAt(0).toLowerCase());
//Other Stuff....
}
}
}
}
I have a menu item attached to a simple onOpen, that calls menuItem1(). Calling the function prompts the user with a warning that they are about to send emails, then goes about getting data to assign email addresses based on the contents of the sheets. firstLetterLastName is an example.
I'm still not getting the loop to function, is it because I have it between two if statements? (Here is a link to the sheet)
Indeed it is quite trivial. You have mixed up your increment. You wrote
for (var j = 0; j < zw.length; ++j)
which means that you do 1 + i (and we know that at the start i = 0 which means your value will always be 1) instead of using the usual
for (var j = 0; j < zw.length; j++)
which would mean that you do i + 1 and update i, so you will get the expected 0 + 1 1 + 1 etc
EDIT:
First, I recommend instead of something like
if (responseMir === ui.Button.YES) {
// Your For loop
doing
if (responseMir !== ui.Button.YES) {
return
}
and in a similar fashion in the for loop
if (zw[j][9] !== 'Yes') {
break
}
It mostly helps increase readability by not including large blocks of code under a single if, when all you want to do is to stop execution.
Your for loop gets broken because of the mistake here:
teacherEmailMir.push(selValsMir[j][7]);
So your loop will go over once. However on the next itteration, you try to push selValsMir[1][7] which does not exist. Note that each itteration you have var selValsMir = []; inside the loop, which means that for every j selValsMir will always be an empty array. So with the following line
selValsMir.push([zw[j][0], zw[j][1], zw[j][2], zw[j][3], zw[j][4], zw[j][5], zw[j][7], zw[j][22], zw[j][23], zw[j][24]]);
your array will always have selValsMir.lenght = 1 and selValsMir[0].length = 10. So obviously trying to access anything from selValsMir[1] will throw you an error and stop the script right there.
I also recommend looking over the if statements that look at the first and first 2 letters of the name as I believe you can accomplish the same with less code. Always try to streamline. Consider using switch() where you end up using a lot of else if

Trouble with closure in Javascript loop

The process: In the game I'm making, there's a for loop that's supposed to save a value in an array. That value changes with each iteration. The problem: when the loop is done running, every element of the array is identical, all showing the most recent value.
I know this issue is common, and I've made so many different tweaks and attempts at solving it over the past 2 days.
0) I tried separating things into separate functions as much as possible.
1) I tried defining my loop counters with "let" so they would have a local scope.
2) I tried wrapping my assignment in a self-executing function so it would happen immediately, preserving the value of currentlyOn before the next loop iteration changes it. My counter is the variable c.
(function(c2, currentlyOn2) {
onAtSameTime[c2] = currentlyOn2;
return 0;
})(c, currentlyOn);
3) I tried attempt #2 with the added feature of returning a function, which still didn't save the value of currentlyOn. This option isn't a good one for me anyway, because the whole point is that I'm doing some computations ahead of time so my game will have a quick animation loop.
onAtSameTime[c] = (function(currentlyOn2) {
return function() {
return currentlyOn2;
};
})(currentlyOn);
I'm tired of beating my head against this wall. Can anyone explain what I'm doing wrong?
For more details, check out the jsfiddle I made. The problem area is at line 59, using a simple assignment:
onAtSameTime[c] = currentlyOn;
onAtSameTime[c] = currentlyOn; sets onAtSameTime[c] equal to the reference of currentlyOn, since currentlyOn is an array, not a primitive value. That reference gets updated with each iteration. You could work around that by creating a copy of the array before adding it to the onAtSameTime array. Something like onAtSameTime[c] = [].concat(currentlyOn); would do the trick.
See this fork of your JSFiddle: https://jsfiddle.net/L2by787y/
You could make a copy from currentlyOn for assigning to onAtSameTime[c]. This keeps the values, but does not keep the reference to the same array.
onAtSameTime[c] = currentlyOn.slice(); // use copy
"use strict";
function log(text) {
document.getElementById("logbox").innerHTML += JSON.stringify(text) + "<br>";
return 0;
}
function whichSwitchesAreOn() {
var currentlyOn = [],
flickedSet,
flickedOne,
turningOnCheck;
for (var c = 0; c < switchesToggled.length; c++) {
flickedSet = switchesToggled[c];
for (var d = 0; d < flickedSet.length; d++) {
flickedOne = flickedSet[d];
turningOnCheck = currentlyOn.indexOf(flickedOne);
if (turningOnCheck == -1) {
currentlyOn.push(flickedOne);
} else {
currentlyOn.splice(turningOnCheck, 1);
}
}
log("currentlyOn: " + currentlyOn);
onAtSameTime[c] = currentlyOn.slice(); // use copy
}
return 0;
}
var switchesToggled = [[0], [1, 2], [0], [2], []],
onAtSameTime = [];
whichSwitchesAreOn();
log(onAtSameTime);
<div id="logbox"></div>
You say you have tried let?
Did you have let currentlyOn = [] inside of the for loop?
for(var c = 0; c < switchesToggled.length; c++) {
let currentlyOn = [];

JavaScript For Loop Keeps Looping Infinity

I've written the functions below as part of a much larger application for processing FASTA formatted files via a web interface. For some reason it decided to run into infinity when call upon my baseCounts() function from within makePretty(). It might be worth noting that both functions are encapsulated by the same parent function.
The function baseCounts() returns valid data in the form of a 100+ long array, console.log confirms that it is not to blame so the problem has to be with makePretty().
Any help is welcome.
function baseCount(records){
// Count instances of Bases in array
var basecounts = Array();
for (i=0; i < records.length; i++){
var record = records[i];
console.log(record);
var count = [record.match(/A/g), record.match(/T/g), record.match(/C/g), record.match(/G/g)];
var basecount = Array();
for (i=0; i < count.length; i++){
basecount.push(count[i].length);
}
// return array of occurance
basecounts.push(basecount);
}
}
function makePretty(fasta){
// Make FASTA more human friendly
var data = Array();
var basecounts = Array();
var bases = Array();
console.log(fasta.length);
// Generate base array
for (i=1; i < fasta.length; i++){
bases.push(fasta[i][2])
}
basecounts = baseCount(bases); // RUNS INTO INFINITY
for (i=0; i < fasta.length; i++){
var record = Array();
record.push(i); // Add protein number
record.push(fasta[i][0]); // Add NC_#
record.push(fasta[i][1]); // Add base index
_record = fasta[i][2];
var l_record = _fasta.length; // Protein length
//var basecount = baseCount(_record);
var cg_content;
}
}
Your nested loops are using the same variable i, and clobbering each other's state.
for (i=0; i < records.length; i++){
...
for (i=0; i < count.length; i++){
...
}
Use distinct variables, say i and j or better yet pick meaningful names.
Also you should declare the variables (var i) to ensure they're local to the function.
Finally, use ++i, not i++. The former means "increment i" while the latter means "i, and oh by the way increment it". They both increment i, but the latter one returns the old value, which is a special language feature to use in special cases.
You're reseting your variable counter in your inner loop (i).
To avoid this, and future problems like it as well as hoisting issues, I would suggest using the newer functions such as forEach or map. You can also clean up your code this way:
function baseCountFunc(records){
// Count instances of Bases in array
var basecount = [];
records.forEach(function(record) {
var count = [record.match(/A/g), record.match(/T/g), record.match(/C/g), record.match(/G/g)];
count.forEach(function(countElement) {
basecount.push(countElement.length);
});
basecounts.push(basecount);
});
}
Also, I noticed you named your function the same name as your variables, you should avoid that as well.

Javascript function with dynamically generated arguments

Below code :
loop(n times)
create HTML Button Element
count++;
assign onclick event = function(){
openSomething("Value_"+count)
}
so if i create 3 input elements (n=3) and then go back click any of the three buttons then every time openSomething("Value_"+3) only gets called.
why openSomething("Value_"+1) and openSomething("Value_"+2) does not get called?
I am not sure what is going on may be it the scope issue but i dont know much about scope either, any help to push me in the right direction is much appreciated.
My original code
var count = 0;
for(var i =0;i<someValue;i++){
count++;
var button = document.createElement("img");
button.src = "/images/small_button.gif";
button.imageButton = true;
button.srcBase = "/images/small_button";
button.onclick = function () {
selectSomething("someIdText_"+count);};
cell.appendChild(button);
}
Because JavaScript doesn't have block-level scoping of variables, and as a result everything is scoped to the function. That means that when you have code that uses a variable (like your loop counter n or your count variable) at a later point (i.e. after the full execution of the function), it will have its value set to the last value for the loop. You need to create a closure (a new scope for the variable) inside of your loop. Something like this (since you didn't post your actual code):
for(var i = 0, l = list.length; i < l; i++) {
(function(count) {
something.onclick = function() {
openSomething("Value_" + count);
}
})(i);
}
For a more modern approtce use let,
works for firefox, chrome, and node
if you need to target all the browsers, use Anthony approach
for(var count = 0, l = list.length; count < l; count++) {
let count;
something.onclick = function() {
openSomething("Value_" + count);
}
}

Assiging timeout/break on multiple observables in knockout

Bear with me on this one cause this one is a bit tricky for me to explain.
So I have multiple observables assigned, say:-
var self = this;
self.amount = ko.observableArray();
self.data0 = ko.observable([10,11,12]);
self.data1 = ko.observable([1,2,3]);
self.data2 = ko.observable([3,4,5]);
self.data3 = ko.observable([6,7,8]);
self.data4 = ko.observable([9,10,11]);
And there is some button that changes the value on each of them with the following function (what this functions does isn't really important, it's merely to show that there is some change going on in the observables)
self.bindOneByOne = function(){
var self = this;
var i = 0;
while(self['data' + i]){
for(var j = 0, len = self['data'+i]().length; j < len; j++){
self['data'+i]()[j] *= 2;
}
self.amount.push(i);
i++;
}
};
Now what I'm wanting to do is to display the changes as it happens in the UI side, one at a time (first self.data0 and then data1 and so on..) when I call a function (click on a button in this case)
My attempt for that behavior so far:-
self.changeValues = function(){
var i = 0;
while(self['data' + i]){
setTimeout(self['data' +i].valueHasMutated,1000);
i++;
}
}
Shouldn't my code first bind self.data0 first and shouldn't it immediately reflect on my UI? Currently, I'm only seeing changes all at once which is not the behavior I wanted.
Here's the fiddle for what I'm trying to do. (Click on Populate/Change to populate the data and change it after it's been populated...and then Mutate to see the changes on the UI side. You can also see that the data is indeed changing when you press Populate/Change button if you check your console prior to clicking on Mutate button)
The key with the timeout is to capture the loop values (i and J) in a closure with the use of an IEFE(immediately invoked function execution)
for(var j = 0, len = self['data'+i]().length; j < len; j++){
(function(){
//this captures the item to set using the current value of i and J
var itemToSet = self['data'+i]()[j];
setTimeout(function(){
console.log('itemToSet',itemToSet());
itemToSet(itemToSet() * 2);
},1000*(i+1));
})() //the () brackets immediately invoke this function that is also in brackets
}
self.amount.push(i);
I've created a fiddle to show it working, you only need the 1 button to show it really, I have just made the second button update each item individually, rather than all values in the array that the first button does.
Fiddle here
Hope it helps.

Categories