Professional Documents
Culture Documents
Adding a menu 8
Calling functions 19
Troubleshooting 23
Using logs 23
Executions 23
Looking online 23
Language
The coding language developed by Google to work with Google Workspace is Apps Script. It is based
on a coding language used extensively on the web, called JavaScript. When looking for help, anything
connected to working with Google apps needs Apps Script, but if you need help with more general
things such as working with different types of data, you’ll need to look for JavaScript help.
Applications
Each Google Workspace tool has its own App in Apps Script: CalendarApp, DocumentApp, DriveApp,
SpreadsheetApp etc. Any number of these applications can be used in the same script; you simply use
the App that enables you to do what you want to do.
Variables
Variables are how you save something in your code with a name so you can use it again. They can
contain anything from a single ‘value’ (e.g. number or text) to the entire contents of a spreadsheet or
document.
To make your coding easier to follow, you should choose meaningful variable names that tell you what
information it contains. Variable names cannot contain spaces, so a common approach to allow
multiple words is to use ‘camel case’ (starts lowercase, each new word starts uppercase e.g.
camelCase).
The first time you use a variable, you must tell Apps Script it’s a new variable with the prefix var, and
at this point you can also set its value. This is done using an equals sign, which in coding assigns a
value. If I needed to create a variable containing a starting value of zero, the code might be:
var startingValue = 0;
As the name 'variable' suggests, the value of a variable can be changed, e.g.
startingValue = 1;
Data types
Data types tell the computer what kind of data is stored in a variable, such as numbers or text (known
as a string, or a collection of characters that can include numbers and punctuation), or a collection of
values.
Different things can be done with different data types; for example, you can add two numbers
together to get a third number, but adding two strings will concatenate them (join them together).
In Apps Script and JavaScript you can change the data type of a variable simply by assigning it a new
value.
var startingValue = 3;
This variable contains a number, because that’s what you gave it.
startingValue = 'Monday';
This variable now contains a string. It’s the use of quotes (single or double) that makes
it a string.
You will also need to work with collections of values in the form of arrays and objects. These enable
you to store many values in one variable. In Apps Script, you can also store things specific to Google
Apps like a spreadsheet, document body, or slide in a variable.
Arrays
Arrays are a collection of items stored in one variable. In Apps Script the items could be text, a
number or a variable, and you can mix data types in one array. An array will often be the values from
one or more rows of a spreadsheet. The values in the array are enclosed in square brackets.
Arrays can also contain arrays, which creates a 2-dimensional (2-D) array. These contain sets of arrays,
surrounded in square brackets, and then the entire variable is also enclosed in square brackets,
creating:
Items in an array can be referenced using an index number inside square brackets. The index numbers
start from 0, so student[0] would give you 901 and student[1] would give you ‘Charles’.
Tip: Try the Exercises with Arrays to help you get your head around how the index numbers work and
how you can access specific values.
Script Editor
Your code is automatically coloured on input. This is not just to make it look pretty, but helps identify
variables and keywords. As you write code, the Editor will try to indent code as is needed to help you
read it. For example, code within loops or ifs should be indented, to make it clear it is a section. The
Editor will use vertical lines to show the layers of indentation.
When the editing cursor is next to a bracket, both this bracket and its corresponding pair will be
highlighted, so you can check you've closed all brackets.
Autocomplete
As you write code, the autocomplete will suggest Apps Script possibilities. If what you want isn't
listed, it may mean you're not working with the object class you thought you were. If it doesn't trigger
at all, it may mean that it can't recognise what you're working with, which may mean an error.
You will put the spreadsheet document into a suitable variable - ‘ss’ is a common variable name for a
spreadsheet document.
var ss = SpreadsheetApp.getActiveSpreadsheet();
var ssAnother = SpreadsheetApp.openById(‘1xYBSgcvv6eXfeEVQ... etc’);
var ssAnotherOne = SpreadsheetApp.openByUrl(‘https://docs.google.com… etc’);
You can find the ID from the URL in the browser address bar when a document is open:
The variable now contains the entire spreadsheet document for the script to make use of.
First obtain the spreadsheet and sheet containing the range of cells, then either:
The Data range is the collection of cells that the script identifies as containing data. If you use this
you don’t need to set the start or finish points of your data, but you might get a much larger array
than you need if you have a lot of data:
Specifying 3 or 4 values will get a 2-D range, starting from startRow, startColumn
Note that getDataRange() and getRange() do not read the value(s) from the cell - you must explicitly
get the value.
This is used to add one row of values to the next available row of the destination sheet. The values,
however, must be in a 1-dimensional array, whereas getRange(...).getValues() will result in a
2-dimensional array. To get a 1-dimensional range of values, you must get the first element of the 2-D
values array (that’s the [0] at the end of the first line):
Having got the source sheet and destination sheet, get the values in the defined source range, then
set these values in the destination range. For this to work, the source and destination ranges must be
the same dimensions - numCols and numRows must be the same.
destinationSheet.getRange(row, col).setValue(theValue);
Deleting data
Both rows and columns can be deleted from a sheet very easily, using just the row or column number.
Deleting removes the entire row/column and closes up the ‘gap’. You must first have obtained the
sheet:
dataSheet.deleteRow(rowNum);
dataSheet.deleteColumn(colNum);
Both these also have ‘plural’ versions for deleting several rows or columns:
dataSheet.deleteRows(rowNum, numRows);
dataSheet.deleteColumns(colNum, numRows);
To avoid this, you can add SpreadsheetApp.flush() after you update a spreadsheet in code, which
forces any updates to appear. Putting this inside the loop after you write the value means you will see
each value appear one by one, rather than at the end, which makes it easier to see progress.
Adding a menu
Custom menus allow you to run scripts from the spreadsheet, instead of opening the script editor. This
is much more user-friendly and avoids anyone accidentally changing the scripts. The menu must be
built by running a function each time the spreadsheet opens, but you can do this by naming the
function onOpen() and it is then always run when the spreadsheet file opens.
There are several ways of constructing menus; this one generally works fine for simple menus:
function onOpen() {
var ui = SpreadsheetApp.getUi();
ui.createMenu('MenuName')
.addItem('First item name', 'first item function name')
.addToUi();
Note the ‘punctuation’ - the last 4 lines are all one expression.
In this context:
Opening a document
If a document (docs, sheets etc) already exists, open it from either its URL or ID. You will normally
need to put it into a variable:
You must first open the document - you can do this from the URL or ID, or you may have newly created
it or copied from a template. You then need to get the document body and then you can append
paragraphs to it. Paragraphs can be included in the expression, but it is often more useful to construct
the paragraph first:
You will need to know the IDs for the template file and for the destination folder (these may be in
existing variables).
Note: if you're making the document in a separate function, you can open the template file and send
it to that function, but you'll need to only send the folder ID to the second function, and then use the
DriveApp in that second function to open the folder, to ensure that Google gives the right Drive
authorisation.
First, get the template file and destination folder using the DriveApp:
template.makeCopy(fileName, destinationFolder)
You will frequently also need the URL or ID of the new document, and this can be obtained in the
same step:
documentBody.replaceText(‘text_to_find’, ‘replace_with_this’);
Both the find and replace text can be either literal text (in quotes) or a variable containing text.
One common use is replacing placeholders in a document with values taken from a set of data. If the
variable studentName contains ‘Eric’, you could replace a placeholder of <<name>> in a document
body using:
documentBody.replaceText(‘<<name>>’, studentName);
One kind of loop is a 'for' loop, which runs 'for' a set of values. A ‘for’ loop looks like this:
The portion in brackets defines the values the loop should run 'for' and contains three critical parts:
var i = 0 ‘i’ is first defined as a new variable, to be used as a counter, with an initial
value of zero (variable name can be anything, but ‘i’ is typically used)
i < someValue this defines a condition, and the loop will continue as long as it is true -
usually testing for ‘i’ reaching a certain value (often the size of your
dataset)
i++ indicates that on each pass of the loop the value of ‘i’ is increased by 1
i++ is shorthand for i = i + 1
The code inside the loop can then make use of the changing value of ‘i’ to define which row/array
value is used on each iteration.
Tip: You can find out the size of an array using .length which allows you to store the 'length' of an
array (so how many items it has in it) in a variable and then use this as the condition for the loop to
test, so the loop never runs for longer than the amount of data you have.
Example
I have a 2-D array called myData, which is a range from a spreadsheet, and I want to repeat an action
for each row. I first need to find out how many rows there are, and then loop up to this value:
In this example, myData[i][0] will always get the array value from the first column, but the row will
depend on ‘i’.
‘If’ conditional
Conditions are used to decide if a portion of code should run. A statement is evaluated and, if true,
the code inside the curly braces will run. The syntax is:
if (condition to test) {
one or more lines of code
}
Examples of conditions:
name != ‘eric’ is the content of the variable name not equal to ‘eric’?
shoeSize > 6 is the content of the variable shoeSize greater than ‘6’?
shoeSize <= 6 is the content of the variable shoeSize less than or equal to ‘6’?
Tip: the use of a double ‘==’ for ‘equal to’. This is because a single ‘=’ in Apps Script assigns a value
to a variable.
&& And (shoeSize > 6 && shoeSize < 10) any value above 6 but below 10 is true
if (condition 1) {
code block 1
} else if (condition 2) {
code block 2
} else {
final code block
}
● The GmailApp gives access to all aspects of your email account - message threads, labels,
attachments etc
● The MailApp script service is designed specifically for sending email and finding your remaining
daily quota - nothing else
If you need simply to send email in a script, usually the MailApp is the best choice.
There are several ways of using it, but all use the same method. The difference lies in what other
parameters you provide. The most useful syntax is:
options these let you define cc/bcc addresses, set noReply, set a reply address,
define an html body etc
All four parameters can be written directly into the statement, but you are likely to use variables for
some (if not all), especially the options.
In this example, userEmail and mailBody are both variables defined elsewhere in the script; options
is defined specifically for sending the email, and sets ‘noReply’ and a cc address.
This portion of script will get all the Form values, extract two individual values and then determine
which spreadsheet row the data is written to.
function onFormSubmit(e) {
// get the collection of Form values
var formValues = e.namedValues;
// get Email address and Date
var userEmail = formValues[‘Email address’][0];
var date = formValues[‘Date’][0];
// find row the values have been written to
var row = e.range.getRow();
…
}
The function must include the event object as a parameter; this is usually shown as an ‘e’ for
‘event’. Many users will name the function onFormSubmit, but this is not essential as the Trigger
determines which function is run.
e.namedValues can be used to obtain all the values submitted on the Form.
Individual values are then obtained using the Form field names (and a [0] to show you only want the
first item in the array):
formValues[‘field name’][0]
In the example above, Email address and Date are Form fields and column headers in the spreadsheet.
The Form values can also be obtained as a 1-D array, but the method above allows you to retrieve
values using the field names, rather than the order on the Form.
The event range (e.range) is the range of cells the data is written to on the spreadsheet. This means
you can find the row number using:
e.range.getRow()
Finding the row is very useful if you need to write extra data to the correct row in the script.
Setting Triggers
A trigger runs a function in response to a specified event. Some triggers (‘simple’ Triggers) are
permanent: a function called onOpen() will always run when a document is opened. Others
(‘installable’ triggers) need to be set by the user.
Triggers can be configured manually or by using scripts; to make a script run when a Form is
submitted, you would set it manually, as it only needs setting once.
This trigger will now run the defined function each time the Form is submitted. Bear in mind,
however, that the script still needs to be authorised before the Form is submitted for the first time.
Plus if you add any further Apps (DriveApp, Spreadsheet App, etc) you will need to go into the Trigger,
‘Edit Trigger’ and click ‘Save’ again - to get the authorisation popup.
SpreadsheetApp.getActiveSpreadsheet().toast('string message');
If you’ve already got the active spreadsheet, you can use ss.toast(message) instead. You can also add
additional parameters to toast() to also display a title for the message and a number of seconds to
show it for.
Setting permissions
Permissions are set for a ‘file’ or ‘folder’, which means you must use the DriveApp, not the
Spreadsheet or Document App. One quirk is that you must get the file by its ID, not the URL.
The permissions are then set on this folder/file, using the method for the level of permission needed.
Files can have viewers, commenters and editors:
newDocFile.addCommenter(emailAddress);
newDocFile.addEditor(emailAddress);
For adding multiple users, there are plural versions of these methods too, which need an array of
email addresses. If a user already has access, adding higher permissions will promote them.
Removing access
Again, it works the same for files and folders:
newDocFile.removeViewer(emailAddress);
newDocFile.removeCommenter(emailAddress);
newDocFile.removeEditor(emailAddress);
For more details, see the File class and the Folder class in the Apps Script Reference.
Calling functions
Long, continuous scripts can be difficult to work with. Creating several separate functions makes
things more manageable and can enable them to be used in different contexts: a function that creates
a Calendar event from a single spreadsheet row could be used to create one from the ‘current row’,
or called from within a loop to create multiple events.
When you 'call' a different function, you can 'pass' values to it that you've already got, like variables.
Values generated in the ‘called’ function can then be ‘returned’ (passed back) to the ‘calling’ function
and stored in a variable for further use.
In this example script, one line of code calls the function ‘calendarEvent’, passing the values for the
event title, start date-time, end data-time and guest emails.
The function ‘calendarEvent’ uses these passed values to create an event and then returns the eventId
to the original function.
Format
If a spreadsheet cell contains the date 08/05/1997, when logged in a script or written from script to a
document, it will appear as something like:
When written to a spreadsheet cell, however, it will use the default date format and look OK.
This means you may have to reformat a date to write it to a document; this is done using an Apps
Script Utilities service and has the syntax:
new Date(suppliedDate) This creates a new date object using the date you supply
timeZone To ensure the correct date, the time zone needs to be supplied.
This can be obtained using the Session service:
var timeZone = Session.getScriptTimeZone();
format This is a text string (in quotes) of the desired date format, using
d, M, y for the date elements and h, m, s for the time.
If you have a script that works with dates/times frequently, one approach is to write a short function
that can be called whenever you want to format a date/time:
...where date is either a variable containing a date, or could be new Date() for the current date/time.
If I have a Date value (eg collected from a Sheets file or Form) and separate Times for start and end,
these must be combined to make two Date-Time values if I want to use them to create a Calendar
event.
The process is a bit long-winded, as it involves several steps to make the Date-Time objects and then
set the hours and minutes. Once you’ve got the code, however, you can just adapt it each time you
need to convert into Date-Times.
If we have three variables eventDate, startTime and endTime, collected from a Sheet/Form:
This will create an event, but if you may wish to do something else with it (like delete it) it is useful
to record its ID, so a better way is:
You create a new ‘object’ containing label-value pairs; curly brackets are used. You just include the
options you need - you don’t have to set all of them. In this example, we already have variables
containing the description, location and a comma-separated list of guest email addresses.
‘sendInvites’ is set to false in this case, so an email invite will not be sent.
//configure options
Troubleshooting
Everyone who works with Apps Script will come across errors and bugs in their scripts frequently. It is a
crucial part of writing code in any language, and there are a number of ways to get help.
Using logs
You can tell your scripts to ‘log’ certain values as a way of checking what is happening at certain
points in the script. For example, you can log the value of a certain variable to ensure that you’re
picking up the values you expect.
When your script has an error or you don’t get the result you expect, checking the logs if you’ve
logged values can be a useful way of checking that you’re working with the values you expect.
Executions
The Executions page and Execution log shows you each time the script was run along with a duration.
It will display any Logger.log values you have outputted. If the execution fails, it details at which line
in the script that occurred and often has a message saying why the script failed. This can provide clues
about how to fix your script.
You can either view the Execution log from the button at the top of the Script Editor (for the current
'run' of the script) or use the Executions button on the lefthand toolbar to open the Executions page
and see details of all script runs.
Looking online
The Apps Script Developer site is often the best place to start when you’re having issues with Apps
Script, as it allows you to check what different services are supposed to do and what they need. For
example, an error when trying to send an email might involve checking the MailApp pages to check
your syntax and what information is required to use it.
Searching online for the error message you’re getting in the execution log or at the top of the Script
Editor is another good way to try and find a solution for errors. Sometimes the responses can be
daunting, but skimming through answers can sometimes help you think of things to try even if you
don’t understand every word.