Javascript: increment counter variable inside loop - javascript

I am using Selenium and nodejs to iterate through a html table and match a string from an array to the string in the table cell.
My array might be ["Name", "Address1", "Address2", "Address3",...] and the value in tr[1] will try to match with Arr[0], tr[2] with Arr[1] etc.
The html table will have a row for each item in the array, but if there is no data for, say, Address2 then that will not appear.
In that case, tr[3] will find that it cannot match with Arr[3]. Instead of moving to tr[4], I want to see if tr[3] matches with Arr[4], then Arr[5] etc. The items in the table will always be in the same order as the items in the array, so I have no need for any array items "unmatched".
I've posted the whole function in case it is relevant, but the issue seems very simply to be that I cannot get "i = i - 1" to carry the new value into the next loop iteration.
I have proved that I get into the Else section, and that the value of i - 1 is as I would expect at that point.
var retrieveData = function retrieveData(Arr){
j = 0
driver.findElements(webdriver.By.xpath("//*[#class='table-container']")).then (function(rows){
rowCount = rows.length;
console.log("Row count = " + rowCount);
}).then (function(fn){
for (i = 1;i < rowCount + 1; i++){
(function(i){
var element = driver.findElement(webdriver.By.xpath("//div[#class='table-container']["+i+"]/div/strong/a"));
element.getText().then(function(Type){
var typefromArray = String(Arr[j].split(':')[0]);
if (Type == typefromArray){
// Do something
j = j + 1;
} else {
// Do something
i = i - 1 // My problem looks to be here, but may be in the transfer of this back up to the beginning of the loop
j = j + 1;
}
});
})(i);
};
});
};
module.exports.retrieveData = retrieveData;

You are using an IIFE in your for-loop to which you pass the index.
That looks like it was designed to prevent modification of i!
When you do
i = i - 1
at the end of your function, it has absolutely no effect as it only affects the i inside your function.
Here's a good article about variable scopes in JavaScript.
But if you still want to modify i, then one option would be to simply have it be returned by the anonymous function and assigned to the external i variable:
.then (function(fn){
for (i = 1;i < rowCount + 1; i++){
i = (function(i){
var element = driver.findElement(webdriver.By.xpath("//div[#class='table-container']["+i+"]/div/strong/a"));
element.getText().then(function(Type){
var typefromArray = String(Arr[j].split(':')[0]);
if (Type == typefromArray){
// Do something
j = j + 1;
} else {
// Do something
i = i - 1 // My problem looks to be here, but may be in the transfer of this back up to the beginning of the loop
j = j + 1;
}
});
return i;
})(i);
};
That is the solution to the question you asked.
But I'm pretty sure there will be other problems in your code, as you are using a synchronous loop, yet updating the counter in an asynchronously-called function (the function passed to element.getText().then).
I'd suggest you either study a bit about how to handle asynchronous code in JavaScript (you're using Promises right now) or open a more general question about the bigger problem you are trying to solve as there will be some more design hurdles to overcome.
Here's an example of the kind of patterns you may have to use to handle multiple Promises (not meant to be copy-pasted, used ES2015 for brevity):
.then(function(fn) {
Promise.all(
Array(rowCount).fill(true).map((_, i) => i + 1) // => [1..rowCount]
.map(i => {
const element = driver.findElement(webdriver.By.xpath("//div[#class='table-container']["+i+"]/div/strong/a"));
return element.getText();
})
// Here we have an array of Promises that we pass to Promise.all
// so we can wait until all promises are resolved before executing
// the following .then
).then(types => types.filter(type => type === typefromArray)
.map(type => { /* doSomething */ });
});

Related

React - For Loop conditional render - not working and infinite trigger

I am trying to dynamically multiple an item in my render based on a variable kind of like this
<div>
<Multiple
multiple={props.multiple}
base1={props.base1}
exp1={props.exp1}
/>
</div>
const Multiple = (props) => {
let result = "";
let wtf = "";
console.log("test"); // gets triggered
for(let i = 0; i < props.multiple; i++){
console.log("i: "+i); // gets triggered
result.concat("{props.base1} +"); // this doesn't work for some reason
wtf = i; // gets triggered
}
console.log("result: "+result); // result is blank
console.log("wtf:" +wtf);
return <span>{result}</span>;
}
PROBLEM 1: Even though I am entering the for-loop, my result is not being changed and i don't understand why.
Also since I cant get it to work yet, I wanted to ask: If i do it this way, where I am concatenating {props.base1} as a string, when i return it in the render, will it show up as "{props.base1}" or will it render as the variable value?
Here is an example as to what it should look like:
base1 = abc
multiple = 2
resulting render should look like:
abc + abc +
Will concatenating my prop into a string before rendering result it in looking like this instead of the above block?
{props.base1} + {props.base1} +
PROBLEM 2: EDIT ALSO, for some reason everything in the <Multiple> component is infinitely triggering, which I also do not understand why it is happening
You are using concat, which doesn't update the original string, it creates a new string instead. What you could do is either
let result = '';
for(let i = 0; i < props.multiple; i++) {
console.log("i: "+i); // gets triggered
result += `${props.base1} `;
wtf = i; // gets triggered
}
console.log(result);
As far as the infinite loop problem goes, what actually is props.muitlple? Is it an array or a string? If so, you should change your loop to
for(let i = 0; i < props.multiple.length; i++)
Edit: if props.multiple is a number, i < props.multiple should work, you should log the value in your component and check once.
The result string is not being properly appended to
const Multiple = (props) => {
let result = "";
let wtf = "";
for(let i = 0; i < props.multiple; i++){
result += props.base1.toString() + "+"
}
console.log("result: "+result);
return <span>{result}</span>;
}
For the infinite loop I would check it's values before entering the loop to make sure your bounds are properly set.
// add this line before for loop
console.log(props.multiple)
a. change to result.concat('${props.base1} + '); (backtics!!)
b. i think that maybe there is a problem with the props you pass to <Multiple ... >. check again their value, maybe log their value.

Infinite Loop for finding a power set for a string

I'm working on a problem where I need to find all the power set of a given string which are all the possible subsets. I feel like I'm close with my current code but I can't figure out why I'm getting stuck on an infinite loop for my second iteration. I ran it through the debugger but I still can't seem to figure it out even though I'm sure it's very simple. When i = 0 then it goes to the second loop where j = 0 && j < 1 so for example if help is my given str argument then I would expect it to add j + '' and push it into my allSubsets array. The problem is that the j iteration will keep looping and doing j++ and will never stop. I'm not sure why this is. One particular question even if I solve this infinite loop - do I need to update the allSubsets.length in the iteration to keep it updated with the pushed in strings?
var powerSet = function(str) {
let allSubsets = [''];
for (let i = 0; i < str.length; i++) {
debugger;
for (let j = 0; j < allSubsets.length; j++) {
allSubsets.push(sortLetters(str[i] + allSubsets[j]));
}
}
return allSubsets;
};
var sortLetters = (word => {
//convert string to an array
//use the sort to sort by letter
//convert array back to string and return
return word.split('').sort().join('');
})
Everytime you push to allSubSets, the length increases, and thus, your loop never ends. A declarative loop runs on the range of the initial loop. See below for a fix based on your code:
var powerSet = function(str) {
let allSubsets = [''];
for (let i = 0; i < str.length; i++) {
allSubsets.forEach( (_char, j) => { // declarative loop here
allSubsets.push(sortLetters(str[i] + allSubsets[j]));
})
}
return allSubsets;
};
var sortLetters = (word => {
return word.split('').sort().join('');
})
From MDN web docs:
The range of elements processed by forEach() is set before the first invocation of callback. Elements which are appended to the array after the call to forEach() begins will not be visited by callback. If existing elements of the array are changed or deleted, their value as passed to callback will be the value at the time forEach() visits them; elements that are deleted before being visited are not visited. If elements that are already visited are removed (e.g. using shift()) during the iteration, later elements will be skipped. (See this example, below.)
See the fourth paragraph under descriptions: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach#Description

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 = [];

Optional Promise in Coffeescript

I am building a tree of nodes but when I run across a certain value I need to make a rest request to get the child nodes and then build those as well. The below works when I have the values hard coded in for the connector.get request, but when I turn it into a promise the function returns before the promise returns. How can I refactor this to delay returning until all the promises resolve?
buildDom: (viewObj) ->
treeElm = document.createElement('div')
rowContainer = document.createElement('ul')
view = viewObj.data.options[0]
i = 0
while i < view.rows.length
rowElm = document.createElement('li')
rowElm.innerHTML = 'Row ' + i
row = view.rows[i]
cellContainer = document.createElement('ul')
j = 0
while j < row.cells.length
cell = row.cells[j]
cellElm = document.createElement('li')
if cell.view
cellElm.innerHTML = 'View: ' + cell.view
if !cell.view?
connector.get(cell.view).then (res)->
cellElm.appendChild buildDom(res)
else if cell.fieldId
cellElm.innerHTML = 'Field: ' + cell.fieldId
if cell.displayType != 'NONE'
cellContainer.appendChild cellElm
j++
rowElm.appendChild cellContainer
rowContainer.appendChild rowElm
i++
treeElm.appendChild rowContainer
cellElm changes with each iteration of the loop, and so may change during the time that connector.get is running, which would lead to appending to the wrong element. Is that what you're seeing? The fix would be to pass cellElm to a function so there is a stored copy of it that the connector.get call can use. I've inlined such a function below.
if !cell.view?
((element) ->
connector.get(cell.view).then (res)->
element.appendChild buildDom(res))(cellElm)

Can someone please explain to me how the scope works for this array of anonymous functions?

Quite simply, I'd like to know why the call to arr0 seems to drag in the value of i instead of the one stored in the function at that position.
<script>
var arr = [];
for(var i = 0; i < 3; i++) {
//Assign anonymous functions to the array in positions 0 to 2
arr[i] = function() { console.log("function " + i); }
}
for(var i = 0; i < 3; i++) {
//The output for these function calls is correct!
arr[i]();
}
//Here I expected to output: function 0, but instead outputs: function 3 WTF!
arr[0] ();
</script>
Here's the output:
function 0
function 1
function 2
function 3
For the last call, i.e: arr[ 0 ] (); I expected the output to be "function 0", but surprisingly IT'S NOT... Could someone please care to explain why?
Thanks in advance!
Well this is a mixed bunch...
You are using the same i variable (despite "re-defining" it in the second loop, it's still the same i) that is placed in the global scope
As a result, in the second loop, each iteration alters the value of that global i, which results in the
function 0
function 1
function 2
function 3
output.
It was, by the way, absolutely not the expected result, if you used k in the second loop:
<script>
var arr = [];
for(var i = 0; i < 3; i++) {
//Assign anonymous functions to the array in positions 0 to 2
arr[i] = function() { console.log("function " + i); }
}
for(var k = 0; k < 3; k++) {
//The output for these function calls is correct!
arr[k]();
}
//Here I expected to output: function 0, but instead outputs: function 3 WTF!
arr[0] ();
</script>
That would produce:
function 3
function 3
function 3
function 3
and that is the infamous loop problem referred in the link above (in comments).
The reason is that functions, defined in your first loop (which, BTW, you should try to avoid in general case), "close" on the variables that are in the same scope as their definition - i in this case. That means that whenever value of i is changed later on, it will be reflected in that function.
The last example shows it in action - i is changed by the first for loop, so when the loop is finished - it's value is 3. All functions you defined now have the same value of i - 3.
To make the output like this:
function 0
function 1
function 2
function 0
you can do this (not that it's that great, structure wise):
var arr = [];
for(var i = 0; i < 3; i++) {
//Assign anonymous functions to the array in positions 0 to 2
arr[i] = (function(index){ return function() { console.log("function " + index); };}(i));
}
for(var k = 0; k < 3; k++) {
//The output for these function calls is correct!
arr[k]();
}
//Here I expected to output: function 0, but instead outputs: function 3 WTF!
arr[0] ();
That produces the ddesire result.
Here, you define an anonymous function:
(function(index){ ... }(i))
that is immediately invoked with i as a parameter. That parameter is referred to as index in the function body (not that it's important, even if you still called it i - it would work, since the "inner" i would shadow the "outer" one).
That function returns a function that has a different closure - on index, which is not important since index is not avialable after the immediately invoked function exists.
Another way would be using some sort of iterator - map, where supported, would do OK.
It's a common problem, after you've read the duplicate posted in the comments, here's a possible solution:
var arr = [0,1,2].map(function(i){
return function(){
console.log("function " + i);
};
});
for(var i = 0; i < 3; i++) {
arr[i]();
}
arr[0]();
By creating an isolate scope with map we avoid the problem altogether. Using underscore's _.range you could replace your loop patterns with:
_.range(0,10).map(function(index){
...
})
The question linked to in one of the comments will give you the general answer.
But I'll also specifically address what you're seeing here, because it might still be slightly confusing even after understanding the answers to the other question.
The main thing to realize here is that there is only one i variable. Even though it looks like you're declaring it twice with the var keyword, the second "declaration" is essentially ignored and treated like any ordinary assignment. You see, the for keyword does not introduce a new scope in JavaScript. So these two snippets are equivalent:
for (var i = 0; i < 3; i++) {}
And:
var i;
for (i = 0; i < 3; i++) {}
Once you realize that, and you get that the functions you create in the first loop all close over the same i, then you can understand why the second loop appears to be "correct" by your intuition: at the start of the loop you set i to 0, and then after each call you increment it. So even though all of them close over the same i, you're changing its value between calls!
And of course, for that last call, the value of i is still 3 since that's what it was at the end of the second loop and you didn't change it from that.

Categories