Get Sheet By Name with an OR statement - google-apps-script

I currently have a script that looks into multiple spreadsheets and pulls in data from a sheet contained within depending on user input.
Unfortunately there are times when users add new sheets in and do not follow the correct format for example each sheet should have four numbers, however sometimes a user will hit the space-bar before inputting those numbers.
Then when the script runs it will come back with an error because it can not find the sheet its looking for, so I need to build in a OR function, my code is quite long so I will type out an example of where I need the function below.
But for example if we can not find sheet "1234" I need to look for sheet " 1234"
var ss= getActiveSpreadsheet()
var sheet = '1234';
var altSheet = ' 1234'
var s1 = ss.getSheetByName(sheet)
//if sheet can not be found, I need to find altSheet instead

You can get the full list of sheets using getSheets(), then use the find() and trim() methods to find the correct one.
const s1 = ss.getSheets().find(sheet => sheet.getName().trim() === '1234');

Related

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.

Writing a string of multiple date / time to a single cell

I have an array of a couple (the array is up to 10) date/time that I want to write to a spreadsheet using getRange().setValues(). I'm converting the array to a string and it looks correct in Logger.
[Mon Feb 02 14:01:00 GMT-06:00 2015, Tue Feb 02 01:00:00 GMT-06:00 2016, , , , , , , , ]
When I try to write the string to a single cell in a sheet:
target6.setValues(source_range6_values);
I get this error:
Incorrect range width, was 10 but should be 1 (line 84, file "Code")
Edited 4/28/2014 adding entire script:
/**
* Copies source range and pastes at first empty row on target sheet
*/
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
//changing the array values to a string
classDate.toString();
//Building a new variable with the string to be inserted below in the target sheet
var source_range6_values = classDate;
//source_range6_values.toString();
Logger.log(classDate[0]);
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Aadding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
To give a correct answer for your question, i guess i need to know how you get the value of source_range6_values.
One quick guess is you might want to use target6.setValue instead of target6.setValues since you want to write the data into one cell only...
A quick & dirty way would be to replace the commas(with spaces):
source = String(source_range6_values).replace("," , " ");
I've had fun with GAS and variables. Casting it as a String should let you use the string functions on it. If that doesn't work can you share a mock-up of your sheets so I can take a look?
edit:
I had to play around with it a bit, seems google's version of .replace() only replaces the first instance (and doesn't allow .replaceAll() ).
I edited your code starting on line 23:
//Getting the values of the array
var classDate = daterange.getValues().toString();
//Building a new variable with the string to be inserted below in the target sheet
//Google has bugs, .replace() seems to only replace the first instance
//-while {} loop replaces all of them
while (!classDate.equals(classDate.replace("," , " "))) { classDate = classDate.replace("," , " "); };
var source_range6_values = classDate;
All the dates are in one cell if you change only those lines (and no errors).
I appreciate the help you two have given me trying to answer this question. #swimmingwood fixed the actual capture of the data into a string, but it left commas and when I inserted it into the target sheet, it wrote it to multiple cells with an error. It did write to the sheet but the error had you use a CTRL-E (inside the taget sheet) to complete the insert and wrote them into separate cells.
#MickATX suggested the code to replace the commas in the string with a space, which would be fine, but apparently he discovered a Google scripting problem that would only allow for the first comma to be replaced and ignore the rest. Great knowledge never-the-less.
I ended up using a formula in an addition cell in the source sheet that looked like this:
=ArrayFormula(CONCATENATE(REPT(TEXT(H2:Q2,"mm/dd/yyyy hh:mm a")&CHAR(10),H2:Q2>0)))
This formula wrote all the date/time entries provided by the form entry into one cell of the source sheet and ONLY the number of entries (1-10). I then wrote that single cell to the target sheet via the script.
Thanks to #swimmingwood and #MickATX for trying to help me, both provided worthy knowledge.
I've read a couple of strange answers here...
If you write an 2D array to a sheet it will obviously be written accross multiple cells... commas are definitely not the issue but the nature of the object is.
Simply convert your array into a string using .toString() or .join() (the latter providing the advantage you can choose the separator to use) and setValue() (without S) at the place you want.
the commas you see in the logger are only typographic representation of array elements separators...
And, last point : the .join() or .toString() methods return new variables, they don't modify the original value so when you write classDate.toString(); you are not doing anything ...
you should write it like this :
classDateAsAString = classDate.toString();
finally your code :
function CopyIt(){
//Establishing source and target sheets
var source_spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var target_spreadsheet = SpreadsheetApp.openById("0AhCv9Xu_eRnSdHpLTkc0d1ZURUtyTU9oRjdFbmpMUFE");
// Get source and target sheets - can be the same or different
var sourcesheet = source_spreadsheet.getSheetByName("Form Responses");
var targetsheet = target_spreadsheet.getSheetByName("Work_Orders");
//Get row of last form submission
var source_last_row = sourcesheet.getLastRow();
// Check for answer to Do you need a Flyer Created? If No, end now. If Yes, continue.
var check = sourcesheet.getRange("T"+(source_last_row)).getValue();
if (check == 'Yes') {
//Pulling date(s) from the users form entry (source sheet) into an array
var daterange = sourcesheet.getRange("H"+source_last_row+":Q"+source_last_row);
//Getting the values of the array
var classDate = daterange.getValues();
var source_range6_values = classDate.join(' & ');// using & as separator for example
// Get the last row on the target sheet
var last_row = targetsheet.getLastRow();
//Setting the target cell in the Marketing Work Order sheet
var target6 = targetsheet.getRange("U"+(last_row+1));
// Adding a new row in the target sheet
targetsheet.insertRowAfter(last_row);
//Inserting the values of source_range6_values into the target sheet. Unfortunately it does not enter the data into the same field and it's in military time.
target6.setValue(source_range6_values);
Logger.log(source_range6_values);
}
}
Now if you want to format the dates in a more civilized way, that should be handled a bit differently... let me know if you still need it / want it.

Google Script to Clear multiple ranges.

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.

Google Docs: Get Sheet Without Name or ID

I'm writing some scripts for a client with the end goal of complete autonomy -- when complete, the spreadsheet will always work forever. Ideally, anyways.
Because I need information from other sheets, I have to access them in a way other than .getActiveSheet(). Because the client might re-name or re-order the sheets, I have to access the sheet in a way that works even after those changes. This rules out getSheetByName() and getSheets()[SHEET_NUMBER] (again, the client might re-name or re-order the sheets). However, it should be possible because of the "gid." Each sheet has a different gid and they do not change when you re-order or re-name the sheets (scroll to the end of the URL for each sheet to see what I mean).
All of the URL accesses only open the FIRST sheet. For instance,
SpreadsheetApp.openById(SHEET_ID).getDataRange().getValues()
returns the values of the first sheet, even if I include the "gid" part at the end. Same with openById and openFile.
So my question is, how do I access a sheet in a way that will work even after renaming the sheet or reordering the sheets within the spreadsheet?
There's no getSheetById method, but you can build your own using getSheetId(). Here:
function sheetsIdMap() {
var sheetsById = {};
SpreadsheetApp.getActive().getSheets().forEach(function(s){ sheetsById[s.getSheetId()] = s; });
//just checking that it worked
for( var id in sheetsById )
Logger.log(id+' - '+sheetsById[id].getName());
//usage example
var sId2 = sheetsById[2];
Logger.log('\n'+sId2.getName());
}
-- edit
Let's try a more straightforward function (although I don't like to do such a loop and don't store the data on a map for subsequent use o(1)).
function getSheetById(ssID, sheetID) {
var sheets = SpreadsheetApp.openById(ssID).getSheets();
for( var i in sheets )
if( sheets[i].getSheetId() == sheetID )
return sheets[i];
return null; //sheet id not found in spreadsheet, probably deleted?
}
Yes there is a sheet id. Its sheet.getSheetId. this id can be used from apps script and can also be transformed into a "real" gid for making a sheet url. Do (sheetId ^ 31578).toString(36) to get the gid.
I had to reverse-eng it to get it and I cant guarantee it will work forever.

Script to copy and sort form submission data

I'm using Google forms to create a spreadsheet that I want sorted automatically by datestamp Z-A. The sorting will be triggered whenever anyone fills out a form.
I think the way to do it is:
ask if there is a "copy of Form responses" on spreadsheet...
if yes, clear all contents...
else...
copy spreadsheet to "copy of form responses"...
sort according to timestamp
Below is what I've cobbled so far. It works only the first time a response is recorded. I'm not a coder so any help is appreciated. If someone could also point me to a list of commands with basic syntax I'd be grateful.
function CopySheet() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var msheet = ss.getSheetByName("Form Responses");
msheet.copyTo(ss);
var CopySheet = ss.getSheetByName("Copy of Form Responses");
CopySheet.sort(1, false); // here 1 is for column no. 1 that
// is "Column A" and true is for ascending, make it
// false if you want descending.
};
You can accomplish this without a script, by using QUERY() in the copy sheet. For instance, if you put this function in cell A1 of your copy sheet, and substitute the key for your form response spreadsheet, you'll end up with a reverse-timestamp-sorted copy of the responses:
=Query(ImportRange(spreadsheet_key,"Form Responses!A:Z"), "select * order by Col1 desc")
This data will be refreshed periodically (~5 mins), so it will reflect new form submissions, but not in real-time.
Note: When using ImportRange() as source data for Query, you need to refer to columns in the Query string using ColN notation.
Alternatively, you could produce a form submission trigger function in the spreadsheet receiving the form submissions, and have it copy the sorted form responses to your copy sheet. The following function does that. You need to set it up as a trigger function for Spreadsheet Form Submission Events. For information on how to test such a function, see How can I test a trigger function in GAS?.
function copyFormSubmissions(e) {
var sourceSheet = e.range.getSheet();
var data = sourceSheet.getDataRange().getValues();
var headers = data.splice(0,1)[0]; // remove headers from data
data.sort(reverseTimestampOrder); // Sort 2d array
data.splice(0,0,headers); // replace headers
var destId = "--copy-sheet-ID--";
var destSheet = SpreadsheetApp.openById(destId).getSheetByName('Sheet1');
destSheet.clear();
destSheet.getRange(1,1,data.length,data[0].length).setValues(data);
};
function reverseTimestampOrder(a,b) {
// Timestamp is in first (zero-th) column
return (b[0]-a[0]);
}
If someone could also point me to a list of commands with basic syntax I'd be grateful.
The Google Apps Script API classes and methods reference is here. If you're learning, try the tutorials (same place), and I recommend you get familiar with Javascript through some form of e-learning - CodeAcademy.com is a good place to start, since it introduces all the language constructs without focusing on web page development, making it very relevant for Googls Apps Script.