For loop keeps breaking out after 1 instance - Apps Script - javascript

I have looked everywhere I can think of for anything that can provide an answer to this. First time posting a question here - I can usually find my answers. I have a for loop that is pulling information from a range of data that is formatted in one cell like this: 09/01/2016 - Status changed to active.
The loop is supposed to first see how many values are in that column then go one by one and split the data into a simple array, post it into two columns on a separate sheet, then move onto the next one. The problem is that it stops after the first entry.
var numEntries = dataSheet.getRange(1,i+1,1000).getValues();
var lastEntry = numEntries.filter(String).length;
if (lastEntry == 7) {
// no change data to date
sheet.getRange(18,3).setValue("No changes yet");
} else {
var changeData = dataSheet.getRange(8,i+1,lastEntry-7).getValues();
for (var y = 0; y < changeData.length; y++) {
var changeHistory = changeData[y][y].split(" - ");
sheet.getRange(nextRow+1,2).setValue(changeHistory[0]);
sheet.getRange(nextRow+1,3).setValue(changeHistory[1]);
nextRow++;
Logger.log(nextRow);
Logger.log(changeData.length);
Logger.log(y);
}
}
I know that it is executing properly because it is properly setting the "No changes yet" value when there are no entries. Variable nextRow starts at a value of 17, and the log properly shows changeData.length with the number of entries and y being equal to 0. But then it stops. It doesn't follow the loop that y is still less than changeData.length. Any help is very much appreciated!
[edit] - I also want to point out that it does properly split and populate the first value into the two cells I want it to, so the whole for statement does work, it just doesn't loop. [edit]
[16-09-29 15:37:48:514 CDT] 18.0 [16-09-29 15:37:48:515 CDT]
11.0 [16-09-29 15:37:48:515 CDT] 0.0

changeData is an n*1 Array.
You are increasing y and and you are also trying to get the y-th column from changeData.
After the first iteration this is undefined because there's only one column.
undefined does not have a split method throwing an exception and terminating the script. (You may not see this exception, these kinds of exceptions aren't always shown to the user for some reason)
Try
var changeHistory = changeData[y][0].split(" - ");
instead.

Related

jQuery append fails on first pass of for loop (but succeeds on others)

I'm using a for loop to iterate through data from a weather API. Within that for loop, I'm assigning each night a rating on its suitability for stargazing (var totalRanking). Then I update a series of divs, having IDs day0, day1, etc., with this rating. (If it matters, these divs are being generated dynamically.) Here's the code block in question:
var dayRankLine = $("<div>");
var rating = $("<span class=rating>"); // happens
rating.text(Math.round(totalRanking * 100) + "%"); // happens
dayRankLine.html("Score: "); // happens
dayRankLine.append(rating); // doesn't happen on 0. (does on others)
$("#day" + i).after(dayRankLine); // happens (even on 0)
As shown in the comments, the first div, #day0, is getting text Score:, but no number. The remaining divs are working as expected. A later code block uses the rating for day 0 to update a different DOM element, and works as expected, showing that the correct value is getting input.
What's different about the first iteration through the loop, and what's different about appending, that that line should fail on that pass only?
Ohhh nevermind. As I said, I was using rating in a lower code block, and that was "stealing" it from day0. rating.clone() and problem solved.

arr.splice() is timing out in loop - better way to replace arr[i]?

So I am having an issue with my solution, and I may be entirely off on what needs to be done. I keep timing out which makes me believe I have the .splice() in an incorrect location.
The problem:
You are given an array of integers. On each move you are allowed to increase exactly one of its element by one. Find the minimal number of moves required to obtain a strictly increasing sequence from the input.
Example
For inputArray = [1, 1, 1], the output should be
arrayChange(inputArray) = 3.
My Pseudo code
First check and see if the current index is greater than the next index. If not, continue check through the entire loop. If so, add one to the next index and test again until true. If you increment the next index by one, add one to a variable "moves". Return moves
function arrayChange(inputArray) {
for( var i = 0; i < inputArray.length; i++){
var addition = (inputArray[i+1]+1)
if(inputArray[i] >= inputArray[i+1]){
inputArray.splice(i,0, addition);
}
}
return inputArray;
}
My Error:
Execution time limit exceeded on test 1: Program exceeded the execution time limit. Make sure that it completes execution in a few seconds for any possible input.
Why your code fails:
for( var i = 0; i < inputArray.length; i++){//iterate until reaching end of array
var addition =(inputArray[i+1]+1);
if(inputArray[i] >= inputArray[i+1]){ //if its not increasing
inputArray.splice(i,0, addition);
}
}
Lets examine a sample input:
arrayChange([1,1]);
So inputArray[i] is 1, and inputArray[i+1] is 1.
Therefore it will go into the if, as 1>=1. So it will add 1+1 into the array,but not at the end but at i, so at position 0. It will look like this:
[2,1,1]
The loop will go on:
[2,2,2....,1,1]
It will never end.
To make it work, you must stop at array.length-1 :
for( var i = 0; i < inputArray.length-1; i++){
and you must insert at the right index,and also remove one:
inputArray.splice(i+1,1,addition);
Non array changing approach:
function numchanges(array){
var before=array[0],counter=0;
for(var i=1;i<array.length;i++){
if(before>=array[i]){
//we need to change array[i] to before+1, so:
counter+=(++before)-array[i];
}else{
before=array[i];
}
}
return counter;
}
console.log(numchanges([1,1,1]));//should return 3
How it works:
A proper strictly monotonically increasing function would have values like this:
[1,2,3,4,5,10]
So it might at least go one up, or it jumps up. So lets take one random array, and its valid counterpart:
[1,1,3,4,5,-1]
[1,2,3,4,5,6]
So the changes needed:
[0,1,0,0,0,7] => 8
So the upper code keeps the valid number ( before ) and the needed change while iterating from left to right. current starts with the first array item:
before=array[0];//1
But we dont need to change the first array element, so we start at i=1.If this number is valid, so
before<array[i]
we just go on:
before=array[i];
i++;//due the for loop
If not, its not valid and we need a correction.
result+=before+1-array[i];
So if the last was 5 (=before) and now weve got -10 (=array[i]), we need to correct it to 6 (=before+1);
result+=6--10//==16
So we need another 16 corrections...

How to tell if two cells are equal in Google Apps Script for Google Spreadsheet

I am writing a script to bind two cells to always hold the same value after every edit. My script so far looks like this:
function myEdit(event){
var ss = event.source.getActiveSheet();
var r = event.source.getActiveRange();
var bind1 = ss.getRange("D11:D11").getCell(1,1);
var bind2 = ss.getRange("D10:D10").getCell(1,1);
var editedCell = r.getCell(1,1);
Logger.log("checking");
if (editedCell === bind1){
Logger.log("Condition was Met!");
bind1.copyTo(bind2);
}
else if (editedCell === bind2){
Logger.log("Condition was met!");
bind2.copyTo(bind1);
}
else{
Logger.log("No condition was met.");
}
When I change the value in either of the bound cells, nothing is copied anywhere else. When I check the execution logs, it appears everything went fine
[14-07-21 10:47:06:215 EDT] Execution succeeded [0.884 seconds total runtime]
But when I check the Logger logs, I get
[14-07-21 10:55:49:260 EDT] checking
[14-07-21 10:55:49:260 EDT] No condition was met.
For some reason, the program is not recognizing that editedCell and bind1 are the same object, when I edit cell D11.
By checking the values of bind1 and editedCell, the values are the same so they must be referring to the same cell, but the equality operator is not working to check whether they are the same range.
For some reason, the program is not recognizing that editedCell and bind1 are the same object, when I edit cell D11.
The reason that the identity operator === doesn't work in this case is that what is being compared is the Range, not the content of the cells in those ranges. Like Sandy suggests, you need to get the values first, and compare those.
However, comparison isn't needed, is it? Your stated objective is to ensure D10 and D11 contain the same value after every edit. Since the only edit that can affect either is a change to themselves, you can ignore any edit made to any cells but D10 or D11. Further, if either of them changes, and myEdit() is invoked, you know they are different, so you can just copy the newly changed value to the other cell.
Something like this:
function myEdit(event){
// To check whether we need to do anything, find which cell was edited.
// Get the A1 notation for the changed cell.
var cellA1 = event.range.getA1Notation();
if (cellA1 == "D10") {
// D10 changed, so copy value to D11
event.range.offset(1,0).setValue(event.value);
}
else if (CellA1 == "D11") {
// D11 changed, so copy value to D10
event.range.offset(-1,0).setValue(event.value);
}
// Any other cell, do nothing.
}

MongoDB: cannot iterate through all data with cursor (because data is corrupted)

Update:
The story is off-topic and the title misleading. The problem is caused by corrupted data set, not cursors, or MongoDB itself. But I would rather like to leave this thread here than to delete it, for that it might help other desperate people.
=== Original story starts here ===
It all starts here: MongoDB: cannot use a cursor to iterate through all the data
I was trying to iterate through a cursor in Java, and it fails because my collection has too many records(~250M). I tried to allocate a new cursor and use cursor.skip to jump back in when the cursor gets timed out but cursor.skip itself times out.
#mnemosyn pointed out the right way for me: break the job into two stages: In the first stage, use a projected cursor to pull only the monotonic _id's of the records. Record the _id's, and then store it somewhere else as "checkpoints". During the second stage, I can then access any chunk of records as a checkpoint recorded.
So I wrote a javascript like this:
db=connect("localhost/twitter");
db.jobScheduler.drop();
for(var i = 0;i<16;++i)
{
db.jobScheduler.save({_id:"s"+i,jobs:[]});
}
var c = db.tweets.find({},{_id:1}).sort({_id:1});
var totalCount = c.count();
var currentBatchSize = 0;
var currentNum = 0;
var currentShard = 0;
var startTid = 0;
var endTid = 0;
var currentTid = 0;
while(true)
{
while(c.hasNext())
{
var doc = c.next()
currentTid = doc._id;
if(currentBatchSize == 0)
{
startTid = doc._id;
}
++currentNum;
++currentBatchSize;
if(currentBatchSize == 50000)
{
currentBatchSize = 0;
endTid = doc._id;
db.jobScheduler.update(
{_id:"s"+currentShard},
{$push:{jobs:[startTid,endTid]}});
currentShard = (currentShard+1)%16;
print(currentNum+"/"+totalCount+"("+currentNum*100/totalCount+"%)");
print("["+startTid+","+endTid+"]");
}
}
if(currentNum != totalCount){
var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1});
print("Cursor resetted....");
}else
break;
}
if(currentBatchSize != 0)
{
currentBatchSize = 0;
endTid = doc._id;
db.jobScheduler.update(
{_id:"s"+currentShard},
{$push:{jobs:[startTid,endTid]}});
currentShard = (currentShard+1)%16;
}
Considering that simply pulling _id only would still result in timeout, I added a guard like this:
if(currentNum != totalCount){
var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1});
print("Cursor resetted....");
}else
break;
because when the cursor times out, I don't get an exception but a false cursor.hasNext().
Since I already recorded currentTid when iterating through them, using the range query var c = db.tweets.find({_id:{$gt:currentTid}},{_id:1}).sort({_id:1}); will put me back in position, theoretically. However the poor little program end up like this:
[337242463750201340,345999466677010400]
21800000/253531208(8.598546968624076%)
[345999469818544100,346244305876295700]
Cursor resetted....
Cursor resetted....
Cursor resetted....
It seems to be stuck at the first occurrence of cursor timeout, forever. And the range query is not bringing me back.
Now I'm really confused. Iteration doesn't work. cursor.skip() doesn't work. Range query doesn't work. And what really works with MongoDB? Or is there something I'm doing really wrong?
Any help would be much appreciated!
Update:
I had some discussion with #AsyaKamsky, he helped me to discover the following things:
setting cursor.batchSize() to 10 doesn't work.
The behaviour is not caused by an idle cursor waiting for 10 minutes. The cursor is pulling data rapidly from server, but still gets invalidated.
The real problem is that after it gets invalid in this way, I could never reallocate any usable cursors anymore. All the new cursors refuse to give me data. There is one possible workaround: close the cursor before this happens, and re-allocate one and use range query to jump back.
Experiments ongoing. Updating this thread in real-time :-)
Update: Failed! I tried renewing cursor after reading 50k records every time. It also gets trapped at this magical index 21800000! That's very close to my cursor.skip() failure offset!
Update:
Confirmed the guessing:
c = db.tweets.find().skip(21800000); //works
c = db.tweets.find().skip(21850000); //doesn't work
I'll try binary search on this range to find the magic number.
Update:
Ok... Magic number found.
db.tweets.find().itcount()
->21837006
db.tweets.find().count()
->253531208
Now what? This is really bad.

Index or size is negative or greater than allowed amount (non-negative indices)

While using Firefox, I keep getting the error described in the subject line for this block of code:
for(var i = 0; i < tables.length; i++)
{
var j = rows.length - 1;
while(j--)
{
if(hideDP && tables[i].innerHTML.indexOf(">D<") != -1)
{
if(!platTag && !soulSilverTag && pearlTag)
{
tables[i].deleteRow(j);//ERROR IS ON THIS LINE
}
}
}//end while loop (rows)
}//end for loop (tables)
I suspect that this error is because I'm somewhat new to making reverse loops, but I specifically made a reverse loop in this instance because it made deleting rows from a table easier. Note also that j is something like 24 and i is 0, so they're non-negative. Could someone shed some light on this for me?
EDIT : The full code can be found here.
Strictly working off of the currently posted code, here are the issues I see:
The posted code looks incomplete. Where is rows being initialized? This could cause the stated error.
Given while(j--);   The var j = rows.length - 1; line is incorrect. That is, unless you know that the last row will never need deleting. But if that is the case, then comment the code to make it clear.
For example, if there were 4 rows, the current code initializes j to 3, but because of the location of the -- operator, the inside of the loop sees: 2, 1, 0.   For the code as shown, use var j = rows.length; or add a comment to show that the logic is deliberate.
The 2 if() statements do not depend on j at all! (At least as the code is posted here.) If this is true, then move the conditionals outside of the j loop.
Consider posting the full, unedited, code. Or linking to it on a site like Pastebin.
Update for full script, now that it's been linked to:
Scanning the complete code, it looks like tables[i].deleteRow(j); can be called multiple times for the same row.
The easy solution, that should be done anyway, is to add a continue statement after each row delete.
For extra credit, reanalyze and simplify the flag and if logic too. :)
Update for target page, now that it's been linked to:
Examining the target page, the tables being looped by this script contain nested tables.
That throws off the row count in this line:
var rows = tables[i].getElementsByTagName("tr");
Sometimes making it seem like table[i] has more rows than it really directly owns.
Solution, use the built in rows array; so the line becomes:
var rows = tables[i].rows;
~~~~
While examining the script relative to the target page, a few other issues seemed apparent:
It's not best to loop through all tables. Target just the ones you need. So this:
tables = document.getElementsByTagName("table");
Should be changed to:
var tables = document.querySelectorAll ("div.KonaBody > table.roundy");
...which will select just the 4 payload tables, and not their subtables or the other tables scattered about.
By fine-tuning the initial table selection, the following, problamatic, test is not needed:
if(tables[i].getAttribute("style").indexOf("border: 3px solid") != -1)
Missing var in front of the majorSections initialization.
That error will also be generated if j is >= to the amount of rows in the table, but I'm not seeing the exact problem.

Categories

Resources