Google Script to Clear multiple ranges. - google-apps-script

I have many spreadsheets that get filled out weekly and closed at the start of the new week. I've written a script to go through and clear out all kinds of ranges in a lot of different sheets. See code below. My question is there a better way to do this instead of having to clear ranges one section at a time and have a million clearContent functions? I cannot figure out how to write a function to clearContent where I can list many ranges all in the same function.
function startWeek() {
var confirm = Browser.msgBox('Did you **Close the Week** first?','Pressing YES will clear your week', Browser.Buttons.YES_NO);
if(confirm=='no'){Logger.log('The user clicked "NO."')};
if(confirm=='yes'){
var sheet = SpreadsheetApp.getActive().getSheetByName('INVOICE LOG');
sheet.getRange('A3:M47').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('DAILY INVENTORY');
sheet.getRange('C5:C8').clearContent();
sheet.getRange('D6:I8').clearContent();
sheet.getRange('C10:I10').clearContent();
sheet.getRange('C13:C16').clearContent();
sheet.getRange('D14:I16').clearContent();
sheet.getRange('C18:I18').clearContent();
sheet.getRange('C21:C24').clearContent();
sheet.getRange('D22:I24').clearContent();
sheet.getRange('C26:I26').clearContent();
sheet.getRange('C29:C32').clearContent();
sheet.getRange('D30:I32').clearContent();
sheet.getRange('C34:I34').clearContent();
sheet.getRange('C37:C40').clearContent();
sheet.getRange('D38:I40').clearContent();
sheet.getRange('C42:I42').clearContent();
sheet.getRange('C45:C48').clearContent();
sheet.getRange('D46:I48').clearContent();
sheet.getRange('C50:I50').clearContent();
sheet.getRange('C55:C58').clearContent();
sheet.getRange('D56:I58').clearContent();
sheet.getRange('C60:I60').clearContent();
sheet.getRange('C63:C66').clearContent();
sheet.getRange('D64:I66').clearContent();
sheet.getRange('C68:I68').clearContent();
sheet.getRange('C71:C74').clearContent();
sheet.getRange('D72:I74').clearContent();
sheet.getRange('C76:I76').clearContent();
sheet.getRange('C79:C82').clearContent();
sheet.getRange('D80:I82').clearContent();
sheet.getRange('C84:I84').clearContent();
sheet.getRange('C87:C90').clearContent();
sheet.getRange('D88:I90').clearContent();
sheet.getRange('C92:I92').clearContent();
sheet.getRange('C95:C98').clearContent();
sheet.getRange('D96:I98').clearContent();
sheet.getRange('C100:I100').clearContent();
sheet.getRange('C105:C108').clearContent();
sheet.getRange('D106:I108').clearContent();
sheet.getRange('C110:I110').clearContent();
sheet.getRange('C113:C116').clearContent();
sheet.getRange('D114:I116').clearContent();
sheet.getRange('C118:I118').clearContent();
sheet.getRange('C121:C124').clearContent();
sheet.getRange('D122:I124').clearContent();
sheet.getRange('C126:I126').clearContent();
sheet.getRange('C129:C132').clearContent();
sheet.getRange('D130:I132').clearContent();
sheet.getRange('C134:I134').clearContent();
sheet.getRange('C137:C140').clearContent();
sheet.getRange('D138:I140').clearContent();
sheet.getRange('C142:I142').clearContent();
sheet.getRange('C145:C148').clearContent();
sheet.getRange('D146:I148').clearContent();
sheet.getRange('C150:I150').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('FOOD INVENTORY');
sheet.getRange('D5:F615').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('LIQUOR INVENTORY');
sheet.getRange('D6:G361').clearContent();
sheet.getRange('E365:G520').clearContent();
sheet.getRange('D524:G573').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('DAILY SALES SHEET');
sheet.getRange('B4:H10').clearContent();
sheet.getRange('B12:H12').clearContent();
sheet.getRange('B14:H20').clearContent();
sheet.getRange('B22:H27').clearContent();
sheet.getRange('B29:H30').clearContent();
sheet.getRange('B33:H34').clearContent();
sheet.getRange('B36:H38').clearContent();
sheet.getRange('B43:H44').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('LAST WEEK INVENTORY');
sheet.getRange('E3:E9').clearContent();
var sheet = SpreadsheetApp.getActive().getSheetByName('SAFE AUDIT');
sheet.getRange('C3:P11').clearContent();
sheet.getRange('C14:P18').clearContent();
sheet.getRange('C22:P22').clearContent();
var destination = SpreadsheetApp.getActiveSpreadsheet();
var name = Browser.inputBox('New Week', 'Enter Pub Name & WE Date (ex. SandwichWE02-02-14)', Browser.Buttons.OK);
destination.rename(name)
};
}

Unfortunately there is not currently a Apps Script method to clear multiple ranges with a single function call.
What you might find a little easier to manage is to instead define one or more data structures (even a simple array) containing the Ranges in question. Then you can clear them all by simply looping over the data structure and calling clearContent() on each Range. This would separate the work of keeping track of your Ranges from the work of clearing them. The data structure might also be useful in other areas of your code.
For better organization, you might also make use of the Spreadsheet.setNamedRange() and Spreadsheet.getNamedRange() functions to assign simple IDs to your Ranges. Note that you cannot give more than one Range the same name.

Related

Google sheets appscript to copy tabs to new sheets

I have a google sheet with around 190 tabs on that i need to split into 190 different files
The files need to be named the same as the tab, the contents of the tab need to be copied as values but i also need to bring the formatting accross (just not the formulas).
I have looked around, and through a combination of previous questions and answers plus using the function list help have formed the following code. It actually works for the first few tabs but then throws up an error about being unable to delete the only sheet.
function copySheetsToSS() {
var ss = SpreadsheetApp.getActive();
for(var n in ss.getSheets()){
var sheet = ss.getSheets()[n];// look at every sheet in spreadsheet
var name = sheet.getName();//get name
if(name != 'master' && name != 'test'){ // exclude some names
var alreadyExist = DriveApp.getFilesByName(name);// check if already there
while(alreadyExist.hasNext()){
alreadyExist.next().setTrashed(true);// delete all files with this name
}
var copy = SpreadsheetApp.create(name);// create the copy
sheet.copyTo(copy);
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
copy.getSheets()[0].setName(name);// rename first sheet to same name as SS
var target_sheet = copy.getSheetByName(name);
var source_range = sheet.getRange("A1:M50");
var target_range = target_sheet.getRange("A1:M50");
var values = source_range.getValues();
target_range.setValues(values);
}
}
}
I am hoping someone can tell me what i have done wrong as I cannot figure it out at this point. I am also open to better solutions though please be aware I am very much a beginner on google appscript, nothing too complex please.
thankyou
In principle your script correctly adds a new sheet to the new spreadsheet before removing the preexisting one
However, mind that calls to service such as SpreadsheetApp are asynchronous.
And this becomes the more noticeable, the longer your script runs.
In your case it apparently leads to behavior that the only sheet is being deleted before the new sheet is being created.
To avoid this, you can force the execution to be synchronous by implementing calls to SpreadsheetApp.flush().
This will ensure that the old sheet won't be deleted before the new one gets inserted.
Sample:
copy.deleteSheet(copy.getSheets()[0]);// remove original "Sheet1"
SpreadsheetApp.flush();
copy.getSheets()[0].setName(name);
You might want to introduce call toflush()` also at other positions where it is important for the code to run synchronously.

Google Sheet ConditionalFormatRuleBuilder issue with .setrange

New user here and not versed in code much. Im working on a COVID-19 form for our company and looking for some help with conditional formatting issues.
SOW: Current we have a google form with simple questions that employees will fill out each day as they arrive, this data is populated on a google sheet.
Each night a google trigger runs that deletes 200 rows of the google sheet for the next days entries, when that script run its messes up my manual conditional formatting, so im trying to run the delete script followed by a script that will apply the conditional formatting to the new sheet each night so everything is ready for the next day.
This is currently what im running that deletes rows each night:
***function deleteResponses() {
var ss = SpreadsheetApp.openById('Sheet_ID');
var sheet = ss.getSheets()[0];
sheet.deleteRows(2, 200);
};***
This is the conditional formatting script ive added to it and trying to run and getting the following error im hoping someone can help me with.
**function deleteResponses() {
var ss = SpreadsheetApp.openById('Sheet_ID');
var sheet = ss.getSheets()[0];
sheet.deleteRows(2, 200);
};
var sheet = SpreadsheetApp.openById('Sheet_ID');
var range = sheet.getRange('C2:C1010');
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenTextContains('No')
.setBackground('#FF0000')
*.setRanges('C2:C1010')*
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);**
Im receiving this error.
"Message details
Exception: The parameters (String) don't match the method signature for SpreadsheetApp.ConditionalFormatRuleBuilder.setRanges. (line 12, file "Code")"
Anyone that could help me, I would greatly appreciate it!
Conditional rule ranges, are plural and the setRanges takes a list of them. It should be a list of ranges and not a list of A1 notations.
So you have to build the range from the A1 notation first, and then pass it to the rule builder like:
var range = sheets.getRange("A1:A1010");
var ranges = [range]; // You can add more ranges to the same rule
var rule = SpreadsheetApp.newConditionalFormatRule().setRanges(ranges)...
Note: Notice you are passing in a list of range objects.
References:
setRanges()
I just wanted to be clear. I think #Aerial is correct but I wanted to point out where I think your problem was:
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenTextContains('No')
.setBackground('#FF0000')
*.setRanges('C2:C1010')* <= Problem is here
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);**
As shown in this example in the documentation. Set Ranges requires an array of Ranges not an array of A1 notations or just one A1 notation. It could take one range such as [range1].
var rule = SpreadsheetApp.newConditionalFormatRule()
.whenNumberBetween(1, 10)
.setBackground("#FF0000")
.setRanges([rangeOne, rangeTwo]) <= Notice the array of ranges
.build();
var rules = sheet.getConditionalFormatRules();
rules.push(rule);
sheet.setConditionalFormatRules(rules);
reference to example
I'd say that many problems here at SO suggest that many programmers rely on their memory of the documentation too much. I find it better to refer to the documentation regularly so that when I get to the end of writing code for a while I can spend less time debugging it.
Pay particular attention to return types and parameter types.

Can I add a formula to a google form response using Apps Script?

Apologies as I am a beginner to coding. I am interested in using Google Apps Script to automate the analysis of a Google Form response.
The simple example I have is for the spreadsheet of responses for a form asking people:
1) how many players there were?
2) where they finished [1st, 2nd, etc,]
On submission of the form I want to run a script that calculates how may points they received and inserts this value in the next available column (column E in this example).
I have tried writing my first Apps Script to automate this process, but without success.
SAMPLE CODE:
function onFormSubmit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Form Responses Master");
var players = e.values[2];
var place = e.values[3];
var positionPoints = e.values[4];
var positionPoints = (players - place + 1);
return positionPoints;
}
I know there are workarounds available by creating duplicate pages, but I was hoping someone might be able to advise me on how to code a solution in App Scripts, in the hope I might get a better understanding of the coding process.
You can write your appscript in the response collector spreadsheet itself instead of writing your code in form's script editor.
So, go to your response sheet and paste this code.
function myFunction()
{
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("Form Responses Master");
var rowNo = sheet.getLastRow();
var colNo = sheet.getLastColumn();
sheet.getRange(rowNo, colNo).setValue("=(C"+rowNo+"-D"+rowNo+")+1");
}
Now, go to Resources -> Current project triggers. Click on add new and set these values in drop downs: myFunction - From Spreadsheet - On form submit.
And you are done. Whenever a new response is submitted, position points will be calculated automatically.
Here,
variable sheet gets your active spreadsheet for different sheet operations which you can perform.
rowNo and colNo as seen in the code, simply fetches the value of last row/column respectively of spreadsheet in which something is written.
And, to set formula in column 'E', you can use setValue. Hence, "=(C"+rowNo+"-D"+rowNo+")+1" will be converted to (C2-D2)+1 in case of second row and so on for next rows.
getRange is simply used to tell the script that write formula inside particular cell.

Importing Data into a spreadsheet and reading the values with Google Script

We use a master spreadsheet containing all the information of the students. I want to create a UI to capture the marks of each student and write it to a Google Sheet from which I will generate their report cards.
I use the following code to import the data from the master list - the names gets imported correctly, but I cannot seem to pull the values? I just get "undefined"
/**
* A function that inserts a custom menu when the spreadsheet opens to generate the Report Spreadsheet.
*/
function onOpen() {
var menu = [{name: 'Capture Report Data', functionName: 'setUpProgressReport_'}];
SpreadsheetApp.getActive().addMenu('Progress Report', menu);
}
/**
* A set-up function that creates a Report Sheet based on the class selected
*/
function setUpProgressReport_() {
var ss = SpreadsheetApp.getActive();
var sheet = ss.getSheetByName('Report 1');
var ui = SpreadsheetApp.getUi(),
response = ui.prompt(
'Enter Class',
'Please enter the class you would like to enter marks for',
ui.ButtonSet.OK_CANCEL),
selectedClass = response.getResponseText();
//Import names of learners by selected class from Master Sheet
var cell = sheet.getRange("A1");
cell.setFormula('=QUERY(IMPORTRANGE("1Dxjt6W54e7n2F8a2zlRZV0n-VtCoPZTC2oZgeMPd8mE","MasterList!A1:Z2000"),"SELECT Col1, Col2,Col4 WHERE Col4 contains ' + "'" + selectedClass + "'" + ' Order by Col2 asc")');
// Freezes the first row to be used as headings
sheet.setFrozenRows(1);
var lastRow = sheet.getLastRow();
var lastColumn = sheet.getLastColumn();
var values = SpreadsheetApp.getActiveSheet().getRange(lastRow, lastColumn).getValues();
Browser.msgBox(values[0][22]);
}
Use SpreadsheetApp.flush() to apply all pending spreadsheet changes before getting the values of cells previously modified by the script.
From https://developers.google.com/apps-script/reference/spreadsheet/spreadsheet-app#flush()
Spreadsheet operations are sometimes bundled together to improve
performance, such as when doing multiple calls to Range.getValue().
However, sometimes you may want to make sure that all pending changes
are made right away, for instance to show users data as a script is
executing.
Also could be helpful to include a test loop to be sure that the IMPORTRANGE task is complete. This test loop could check every certain amount of time,let say 500 millisecondes if certain change already occurred, in example, the script could get the last row before doing the import and compare it with the last row after it and doing a loop until the last is greater than the first.
An alternative is to use Utilities.sleep(milliseconds) alone. This could work but since the IMPORTRANGE execution time isn't deterministic we can not know for sure how many time is required.
I am no expert but I think I kind of figured out what the problem was... well in theory and maybe not in the correct technical details.
var ss = SpreadsheetApp.getActive() sets the current spreadsheet as the ss value, and this is without the imported data. So referencing this variable actually references the data before it was imported. By creating a seperate function and "refreshing" the var ss = SpreadsheetApp.getActive() solved the issue and I could retrieve data normally.

Issue applying script to second sheet

I have no idea how to code. I use to make website on HTML, so my knowledge is limited. I piece together and alter existing codes. I got decent on Excel VBA, but then needed to start using google sheets. So, that said...
I have a spreadsheet with two sheets, 'MIS' and 'Admin'. I have a bunch of code a formula that assigns a value (1,2,3...) based on how many of two drop-down criteria that each line matches (1 if it matches criteria #1, 2 if matches criteria #2, and 3 if it matches both). The code then hides everything and unhides only those with numbers matching the criteria. I need this to be clean and quick, it's for people who can barely use computers.
The problem is, the code only works on the first page. I tried using the same code, tried amending the code, and I tried inserting 'Admin' in about half a million places. Please help. The admin function is my latest attempt. This is where I inserted 'Admin' in a dozen places. Also, if you see anything I'm using that is slowing down the code, I could use some help with that too. There are 6 functions, which basically do the same thing using the same code but corresponding to different number combinations. The one in question is below.
function Admin(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheets('Admin')[0];
var range = sheet.getRange(1, 1, sheet.getLastRow());
sheet.hideRows(8,sheet.getLastRow());
var values = range.getValues();
for (var i=0; i<values.length; i++){
if(values[i][0] === 3){
sheet.showRows(i+1);
}
}
}
This line is your 'only the first sheet' problem, you're sort of combining two methods of defining a sheet:
var sheet = ss.getSheets('Admin')[0];
You can get a specific sheet by name by using the .getSheetByName() method i.e.
var adm_sheet = ss.getSheetByName('Admin');
var mis_sheet = ss.getSheetByName('MIS');
Or you can get a sheet by index
var adm_sheet = ss.getSheets()[0]; //returns first sheet
var mis_sheet = ss.getSheets()[1]; //returns second sheet
Lastly, you can get all of the sheets in your spreadsheet and list them in an array like this:
var sheets = ss.getSheets();
And then use the index of the sheet you need in this array like so:
sheets[0] //the first sheet
sheets[1] //the second sheet