strange server error GAS - loops

I have a function running in google script. However it does not finish, it enters the main loop and then it stops somewhere, no errors. If i look in the execution description it says server error wait a while and try again.
I tried running it several times but it keeps stalling, but each time the last values of i and j are different from the previous stall.
I really can't imagine that this is a real server error, it must be the code.
function start() {
var sheet1 = SpreadsheetApp.openByUrl("xx").getSheetByName('Blad1');
var sheet2 = SpreadsheetApp.openByUrl("xx").getSheetByName('Blad1');
var range1 = sheet1.getRange(1,1,54,26);
var range2 = sheet2.getRange(1,1,10,7);
var teller = 0;
for(var i = 1; i<=range1.getNumRows(); i++){
for(var j = 1; j<=range2.getNumRows(); j++){
Logger.log(i);
Logger.log(j);
if(range1.getCell(i, 8).getValue() == range2.getCell(j, 2).getValue() && range1.getCell(i, 16).getValue() == range2.getCell(j, 4).getValue() && range1.getCell(i, 19).getValue() == range2.getCell(j, 6).getValue()){
range1.getCell(i,25).setValue(range2.getCell(j, 7).getValue());
range2.getCell(j,1).setValue("Script")
teller++;
}
}
}
Logger.log(teller);
};

I imagine you are hitting the SpreadsheetApp api too much. Best practice is to load the data you need into the script and iterate over it. Using your script as an example:
function start() {
var sheet1 = SpreadsheetApp.openByUrl("xx").getSheetByName('Blad1');
var sheet2 = SpreadsheetApp.openByUrl("xx").getSheetByName('Blad1');
var range1 = sheet1.getRange(1,1,54,26);
var range2 = sheet2.getRange(1,1,10,7);
var data1 = range1.getValues();
var data2 = range2.getValues();
var teller = 0;
for(var i = 0, r = data1.length; i < r; i++){
for(var j = 1, c = data2.length; j < c; j++){
Logger.log(i);
Logger.log(j);
if(data1[i, 8] == data2[j, 2] && data1[i, 16] == data2[j, 4] && data1[i, 19] == data2[j, 6]){
range1.getCell(i,25).setValue(data2[j, 7]);
range2.getCell(j,1).setValue("Script")
teller++;
}
}
}
Logger.log(teller);
};

Related

Google Sheets Script: How to run function on loop over all sheets in workbook and onOpen?

I want a script to highlight a cell if another cell is yesterday's date (then remove the previous highlights). I want the script to run whenever I open the spreadsheet and when I switch to another tab.
I originally just had the script run as a .setActiveSelection() or a .getRange("B:B").activate(), but it's easier to notice when the cell is highlighted. But then I have to remove the previous highlights.
I'm trying to get my function to work over all my sheets to highlight a cell (col E) in the same row if the date=today()-1 (col B) and remove the highlight in column E if the date (col B) < today()-1. Right now, I'm using a script to set the highlight and conditional formatting to remove any previous highlights. However, the script only runs on the active sheet, and either has to use the trigger onOpen or I have to set it to run every minute.
I found this solution saying I could use this in my code to run a loop through all my sheets (the first 6 tabs):
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (i = 0; i < 6; i++) {
var sheet = ss.getSheetByName(sheets[i]);
}
However, when I add it, I get an error message saying:
Cannot call method "getRange" of null
Should I just use conditional formatting to highlight a specific date and remove previous highlighting?
https://docs.google.com/spreadsheets/d/1oUPWWA8NbT6pYve3pFvBlKm7PNCj3XxnM020yp-rRHs/edit?usp=sharing
My full function:
function jumpToAmtEaten() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (i = 0; i < 6; i++) {
var sheet = ss.getSheetByName(sheets[i]);
var r = sheet.getRange("B:B");
var values = r.getValues();
var day = 24*3600*1000;
var today = parseInt((new Date().setHours(0,0,0,0))/day);
var ssdate;
for (var i=0; i<values.length; i++) {
try {
ssdate = values[i][0].getTime()/day;
}
catch(e) {
}
if (ssdate && Math.floor(ssdate) == today-1) {
sheet.setActiveRange(r.offset(i,3,1,1)).setBackground("yellow");
break;
}
}
}
}
When you call
var sheet = ss.getSheetByName(sheets[i]);
You're passing a a Sheet object to the method getSheetByName() instead of a name. Also you don't need to keep calling a getSheet method once you've already obtained them once. Try this instead:
function jumpToAmtEaten() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheets = ss.getSheets();
for (var i = 0; i < sheets.length; i++) {
Logger.log("i = " + i);
var r = sheets[i].getRange("B3:B")
Logger.log("r = " + r.getA1Notation());
var values = r.getValues();
var day = 86400000;
var today = parseInt((new Date().setHours(0, 0, 0, 0)) / day);
var ssdate;
for (var j = 0; j < values.length; j++) {
try {
ssdate = values[j][0].getTime() / day;
}
catch(e) {
}
if (ssdate && Math.floor(ssdate) == today - 1) {
Logger.log(Math.floor(ssdate));
Logger.log(today);
Logger.log(i);
Logger.log(sheets[i]);
Logger.log(r.offset(j, 3, 1, 1).getA1Notation());
sheets[i].getRange(r.offset(j, 3, 1, 1).getA1Notation()).setBackground("yellow");
break;
}
}
}
}
Also you can't use the same counter variable in nested loops as you will increment it every time you complete an interation of either one.

Reset checkboxes to false

I have a Google spreadsheet where column A has checkboxes in each row. I have written a script to perform a function on all rows where the checkboxes are checked, but I want to add in at the end a reset function so that all checked boxes are unchecked again after the script is run.
I've tried using a for loop like this:
var dataRange = sheet.getRange('A3:A');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i].setValue(false);
}
}
}
But clearly this doesn't work as I get an error.
Does anyone know how this could be done?
How about this modification? I think that there are several solutions for your situation. So please think of this as one of them.
Modification points :
The reason of the issue is values[i].setValue(false);. values[i] is an array. Please use the range for setValue().
But to use setValue() in the for loop leads to higher cost. So in this modification, I used setValues().
Put "false" to values, if values[i][j] is "true".
Put the modified values to the sheet using setValues().
Modified script :
var dataRange = sheet.getRange('A3:A');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i][j] = false; // Modified
}
}
}
dataRange.setValues(values); // Added
Reference :
setValue()
setValues()
If this was not what you want, please tell me. I would like to modify it.
Update:
You can now use range.uncheck() directly on the range
Alternatively, Since you want to uncheck everything in the range (and all of the range have checkboxes in them), just do:
sheet.getRange('A3:A').setValue(false);
without checking/looping.
By highlighting all the cells in the range that you want to "uncheck" hold Crtl and double tap the spacebar.
The remove-check-marks method is "uncheck"…
var range = SpreadsheetApp.getActive().getRange('A21:A59');
range.uncheck();
This solution posted above does reset all checkboxes;
however, it also unexpectedly changes cells with the value of 1 in it to FALSE.
I want to make a script to reset checkboxes on ALL sheets...
function resetCheckBoxesAllSheets() {
var ss = SpreadsheetApp.getActive();
var allsheets = ss.getSheets();
for (var s in allsheets){
var sheet=allsheets[s]
var dataRange = sheet.getRange('A4:Z100');
var values = dataRange.getValues();
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
if (values[i][j] == true) {
values[i][j] = false; // Modified
}
}
}
dataRange.setValues(values);
}//end of sheets loop.
}// end of function...
I have set one checkbox instead of caption as shown in the picture
I write a script for check the data range and check uncheck all accordingly.
Here is the script.
function onEdit(e){
Logger.log('OnEdit');
var range = e.range;
var cl = range.getColumn();
var rw = range.getRow();
Logger.log('Row = ' + rw + ' Column = ' + cl);
var ActiveSheet = ss.getActiveSheet();
if(ActiveSheet.getName() === 'Companies' && rw == 1 && cl == 5){
var isAllTrue = ActiveSheet.getRange(rw, cl).getValue();
if(isAllTrue == true){
var Avals = ActiveSheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
ActiveSheet.getRange(2, 5, Number(Alast-1)).setValue(true);
}
else
{
var Avals = ActiveSheet.getRange("A1:A").getValues();
var Alast = Avals.filter(String).length;
ActiveSheet.getRange(2, 5, Number(Alast-1)).setValue(false);
}
}
}
Change sheet name as per your need.
Note: create onedit trigger for same function if not trigger auto.

Google Script array value undefined

i wrote a code that should take a value from a cell and than convert it to the string and than into the array. its working fine. I can see the value of arr in Logs as an array with the input of the cell. For example, in the cell are " dog, cat " and tha arr value in Logs is [dog, cat].
But after i create this array, i would like to make a loop on it. And than i become in Logs, that arr is undefined. Can somebody help me please? im working on it for 2 days :(
Here is my code:
function animal (s,z){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var range = sheet.getRange("SM");
var columnNumber = getColumnNumberOfSM(s);
var rowNumber = getRowNumberOfSM(z);
var colAction = columnNumber + 1;
var action = sheet.getRange(rowNumber, colAction, 1, 1).getValues();
var bar = action.toString();
var arr = [{}];
arr = bar.split(", ");
//return arr; // returns an array [dog, cat]
var foo = arr; // underfined
for (var i = 0; i <= foo.length; ++i) {
if (foo[i] == "dog") {
Logger.log(upload());
}
}
}
now i edit my code and its working fine,but only with "dog" but not with "cat"
function animal(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName('Sheet1');
var action = sheet.getRange("C3").getValue();
var bar = action.toString();
var arr = bar.split(", ");
for (var i = 0; i <= arr.length; ++i) {
if (arr[i] == "dog") { // works
Logger.log(upload());
}
if (arr[i] == "cat") { // doesn't work
Logger.log(upload());
}
}
}
You don't need to initiate arr as blank you can directly initialize it as a result to split method , also one point I didn't get is why do you need to assign arr to foo? You can directly iterate through arr.length and perform the required operation.
Here's my working snippet.
var parts = path.split(",");
for (var i = 0; i < parts.length; i++) {
if (parts[i] == 'dog'){
Logger.log(parts[i]);
}
}

TypeError: Impossible to find getCell function in the Sheet object

I'm trying to get some data from a Google Sheet to insert in two arrays with a for loop but I think I'm using the wrong methods.
This is the code:
function regValori() {
var datAgg = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("datiaggiornati");
var f5 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Foglio5");
var CValue = new Array();
var CName = new Array();
CValue.lenght = datAgg.getRange("B1").getValue();
CName.lenght = CValue.lenght;
for (var i = 0; i <= CValue.lenght; i++)
{
CValue[i] = datAgg.getCell(i, 16).getValue();
CName[i] = datAgg.getCell(i+2, 1).getValue();
}
var riga = f5.getRange("B2").getValue();
for (x = 3; x <= numCrypto; x++);
for (i=0;i<numCrypto;i++);
{
f5.getRange(riga, x).setValue(valCrypto[r+i]);
f5.getRange(2,numCrypto).setValue(nomiCrypto[r+i]);
}
}
The error:
TypeError: Impossible to find getCell function in the Sheet object
Thanks for your help!
Errors:
getCell() is a method of Range, not Sheet
Google Sheets starts from rowIndex = 1
If you don't need an offset (i.e. start from a certain cell), then you can use getRange:
for (var i = 0; i <= CValue.lenght; i++)
{
var r = i + 1; // rowIndex
CValue[i] = datAgg.getRange(r, 16).getValue();
CName[i] = datAgg.getRange(r+2, 1).getValue();
}
this is the solution:
function regValori() {
var datAgg = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("datiaggiornati");
var f5 = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Foglio5");
var riga = f5.getRange(2, 2).getValue();
for (var i = 2; i <= 6; ++i) {
f5.getRange(riga, i + 1).setValue(datAgg.getRange("P" + i).getValue());
f5.getRange(2, i + 1).setValue(datAgg.getRange("A" + i).getValue());
}
}
i posted another question here for the same problem and I get a perfect answer how to use for loop to drastically reduce code lenght.
the only part I changed is this one: (var i = 2; i <= 6; ++i) with this one: (var i = 2; i <= n+1; ++i), so it can work perfectly with any variable n to get with command "var numCrypto = datAgg.getRange(1,2).getValue();"

Performance of array reverse in ActionScript 3

I have two code snippets . which one is better.
var texts:Array = new Array(1,2,3,4,5,6,7,8,9,10);
texts.reverse();
for(var index:int=0; index < texts.length; index++) {
trace(texts[index]);
}
Or
var texts:Array = new Array(1,2,3,4,5,6,7,8,9,10);
for( var index:int = texts.length; --index;) {
trace(texts[index]);
}
In former we have reverse operation and then print it and in latter we start from the length and start printing the array. The goal is to traverse the array from last.
I wrote a script which evaluates time elapsed between for loops. After running the application numerous of times, it appears that the 2nd for loop is fastest.
Averaging: 4.168000000000001 | 4.163000000000002 seconds respectively for 100 iterations of each for loop.
The script is as follows:
import flash.events.Event;
var _t:int = getTimer();
var dt:Number;
var started:Boolean = false;
var iteration:int = 0;
var timer1:Number = 0;
var timer2:Number = 0;
var numberOfIterations = 100;
var texts1:Array = new Array(1,2,3,4,5,6,7,8,9,10);
var texts2:Array = new Array(1,2,3,4,5,6,7,8,9,10);
texts2.reverse();
addEventListener(Event.ENTER_FRAME, onEnterFrame);
addEventListener(Event.EXIT_FRAME, onExitFrame);
function onEnterFrame(e:Event):void {
var t:int = getTimer();
dt = (t - _t) * 0.001;
_t = t;
iteration++;
// small FLA load buffer
if(iteration == 50 && !started)
{
iteration = 0;
started = true;
}
}
function onExitFrame(e:Event):void {
if(started)
{
if(iteration < numberOfIterations)
{
for(var index:int=0; index < texts1.length; index++) {}
timer2 += dt;
trace("Time Elapsed Alg 2: " + (timer2));
}
else
{
for( var index:int = texts2.length; --index;) {}
timer1 += dt;
trace("Time Elapsed Alg 1: " + (timer1));
}
if(iteration == ((numberOfIterations*2)-1))
{
trace("________________________________________");
trace("FINAL: " + timer1 + " | " + timer2);
removeEventListener(Event.EXIT_FRAME, onExitFrame);
removeEventListener(Event.ENTER_FRAME, onEnterFrame);
}
}
}
I am interested to see what others get using the following script, as it seems reliable on my PC as if I switch the for loop positions, the results still indicate that the fastest is:
var texts:Array = new Array(1,2,3,4,5,6,7,8,9,10);
for( var index:int = texts.length; --index;) {
trace(texts[index]);
}
The first one doesn't trace the last item in the array ie 1
The second one does.
So I say the second is better.
I'm curious though - what's the correct way to code the first one so that it traces all the items in the array?

Resources