Google Script - exceeded maximum execution time - javascript

I have built a script that pulls from multiple spreadsheets and calculates total values. Basically what happens is multiple employees will input hours performed on a particular task for a particular customer. I then want to calculate how much work was done for a particular customer on a master spreadsheet and pull some analytics.
So on the master spreadsheet, there is a tab for each customer. This function takes the sheet names from the master spreadsheet and creates an array of all of the customer names. The reason why I do this is so that if a new tab is created, that customer name will automatically be included in the customer array.
function getCurrentCustomers() {
var sheets = servicesSpreadsheet.getSheets();
for (var i=0 ; i < sheets.length ; i++) {
if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files") {
currentCustomers.push(sheets[i].getName());
};
};
};
The next function takes a look at all of the files in a particular Google Drive folder and returns the IDs in an array. This allows me to create a copy of an employee spreadsheet stored in this particular folder, and that spreadsheet's values will automatically be calculated as they change.
function listFilesInFolder() {
var folder = DriveApp.getFolderById("0B7zrWIHovJrKVXlsaGx0d2NFT2c");
var contents = folder.getFiles();
var cnt = 0;
var file;
while (contents.hasNext()) {
//Finds the file in the specified folder
var file = contents.next();
//Increases the count
cnt++;
//Gets the Id of the file
data = [
file.getName(),
file.getId()
];
//Appends it to the employeeId list
employeeIds.push(data);
};
return employeeIds;
};
The last function is the one that is slowing it down a great deal.
First I create an array for all of the possible services. Unfortunately there are 137 individual services.
Then I loop through all of the customers. For each customer, I loop through every service to see if it appears on any of the employees spreadsheets. I am thinking there is a more efficient way to do this. The Google Script times out before I even get through one full customer. Also, I haven't even included the spreadsheets for all of the employees yet. I am just testing using dummy data.
function calculateNumbers(){
var allServices = servicesSpreadsheet.getSheetByName("Services").getRange("Z2:Z137").getValues();
Logger.log(allServices);
Logger.log(allServices[0][0]);
employeeList = listFilesInFolder();
//Gets services spreadsheet range
/*Loops through all of the current customers (currentCustomers comes from function getCurrentCustomers)*/
for (var c = 0; c < currentCustomers.length; c++) {
var currentCustomer = currentCustomers[c];
var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn();
var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues();
//Loops through all of the services
var serviceTotal = 0;
for (var service = 0; service < allServices.length; service++){
//Loops through employee spreadsheet Ids
for (var i = 0; i < employeeList.length; i++) {
//Get employee spreadsheet ID
var spreadsheetId = employeeList[i][1];
//Open the employee spreadsheet by ID
var employeeSpreadsheet = SpreadsheetApp.openById(spreadsheetId);
//Get the sheets from the particular employee spreadsheet
var sheets = employeeSpreadsheet.getSheets();
//Gets the name of each sheet in the employee spreadsheet
var sheetsName = [];
for (var j = 0; j < sheets.length; j++) {
sheetsName.push(sheets[j].getName());
};
//Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet
for (var q = 0; q < sheetsName.length; q++) {
if (sheetsName[q] != "Services") {
//Gets the range of the spreadsheet
var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues();
//Loops through the range to see if range matches service and customer
for (var r = 0; r < range.length; r++) {
if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) {
serviceTotal += range[r][4];
};
};
};
};
};
//Adds the service total to the correct customer's spreadsheet
for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){
var end = 0;
if (end > 0) {break}
else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {
servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal);
end += 1;
};
};
};
};
};
The first image shows what an employee spreadsheet looks like. The second shows what an individual customer sheet looks like. There are many customer sheets on the master spreadsheet.
One of the things you will notice, is that each service has a category. I was thinking maybe to check the category and then check the particular service. There are 23 categories.
I was also hoping there was a way to only look at services that have actually had work done on them. If no one has ever done a Campaign Setup, maybe that could be ignored.
Any help would be greatly appreciated. I apologize for the lengthy post!

You are calling services inside of loops multiple times. These can take up to a couple seconds each, and are generally considered to be very slow. This is against Apps Script best practice as it greatly increases your execution time.
TL;DR: You are calling Apps Script services potentially thousands of times. Each call can take up to a couple seconds to execute. You need to cache your service calls outside of your loops, otherwise your performance is going to suffer horribly.
Examples:
1:
if (sheets[i].getName() != "Services" && sheets[i].getName() != "Employee Files")
Create and set a variable with the sheet name once, and check that instead of calling the getName() method twice. This isn't a huge deal, but will increase execution time.
2:
This is a biggie, as it's one level deep in your loop in calculateNumbers
var lastColumn = servicesSpreadsheet.getSheetByName(currentCustomer).getLastColumn();
var servicesRange = SpreadsheetApp.openById("1X3RRR3UVeot-DYCyXOsfVo0DoKjHezltwBPwUm8ZYig").getSheetByName(currentCustomer).getRange("A4:BC227").getValues();
2a:
You are opening a new spreadsheet, opening a new worksheet, and then getting a range for the same sheet, and getting the values of that range once per loop for your servicesRange. These service calls will stack up quick, and bloat your execution time.
2b:
I see you are getting the lastColumn, but I don't see it used anywhere? Maybe I missed something, but it going unused while making a service call for each loop will add even more to your execution time.
3:
This is massive, you are potentially calling Apps Script services thousands or tens of thousands of times here. This snippet is already two loop levels deep.
//Loops through all of the sheets in an employee spreadsheet ignoring the Services spreadsheet
for (var q = 0; q < sheetsName.length; q++) {
if (sheetsName[q] != "Services") {
//Gets the range of the spreadsheet
var range = employeeSpreadsheet.getSheetByName(sheetsName[q]).getRange("A5:E1000").getValues();
//Loops through the range to see if range matches service and customer
for (var r = 0; r < range.length; r++) {
if (range[r][3] == allServices[service][0] && range[r][1] == currentCustomer) {
serviceTotal += range[r][4];
};
};
};
};
};
//Adds the service total to the correct customer's spreadsheet
for (var serviceServices = 4; serviceServices <= servicesRange.length; serviceServices++){
var end = 0;
if (end > 0) {break}
else if (allServices[service][0] == servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,1).getValues()) {
servicesSpreadsheet.getSheetByName(currentCustomer).getRange(serviceServices,6).setValue(serviceTotal);
end += 1;
};
};
};
You have multi-level nested loops in nested loops calling the same services. If you had a thrice nested loop with each level being iterated 10 times, and the bottom level calling a service once per loop. You would be calling an Apps Script service 1,000 times. Being conservative, at let's say 0.5 seconds per service call, you would already have 8.3 minutes of execution time.
Cache your service calls, and perform bulk operations.

Related

Optimization of code while working with range object in excel

I have recently moved for office add-in from vb.net to JavaScript and Office.js. In General, I have observed that JavaScript add-in is way too fast than to vb.net.
In one of the operations, I am not able to get benefit of speed in JavaScript add-in. Maybe it is due to inadequate code that I am using!
I have one master sheet which contains a large table, first column of table is name of every other sheets of that workbook and first row of table have address of cells. Rest of data in table are values to be transferred at the sheet name defined in the first column and cell adress defined in the first row.
In coding, I am creating an array of range object for each value indicated in table and then I run context. Sync () function to restore values in every sheets.
In my real-life application data in table can be 10K to 50K, I can see time taken for this operation is about approx. one minute (for 10K). In contrast of this, I can create the table (master sheet) within few seconds (5 -6 sec) only.
Is there any other workaround or suggestion to reduce time?
/* global Excel, console*/
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
if (arryRange.values[iRow][0] == "SheetName/CellAddress") {
addRow = iRow;
}
if (sheetName.indexOf(arryRange.values[iRow][0]) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
if (arryRange.values[addRow][iCol]) {
const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);
const range = sheet.getRange(arryRange.values[addRow][iCol]);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
First, base on your statement, I assume you have a table in master sheet. This table's heading is like ["sheetName","A13","D23",...] ("A13" and "D23" are examples of cell address). In each row of this table, contain sheet's name and some values. The sheet's name may not related to a real sheet's name(not exist), and values may contain some blank. And you want to set values on other sheets based on the information given by master sheet's table.
Then I have some suggestions based on my assumptions and your code.
Move unchanged value out of loops.
For example, you called const sheet = context.workbook.worksheets.getItem(arryRange.values[iRow][0]);. We can move context.workbook.worksheets out of loops by define var sheets = context.workbook.worksheets and const sheet = sheets.getItem(arryRange.values[iRow][0]). Which could increase the performance.
Also some reused values like arryRange.values[iRow][0], arryRange.values[0][iCol] can be moved out of loop.
Seems you use arryRange.values[addRow][iCol] only for get the address in table's heading. You can replace it by arryRange.values[0][iCol].
Below is the code I rewrite, just for reference, it may not fully satisfy what you need.
export default async function restoreData() {
var allRollback = false;
await Excel.run(async (context) => {
var sheets = context.workbook.worksheets.load("items/name");
var wsdb = context.workbook.worksheets.getItem("db");
const arryRange = wsdb.getUsedRange();
//var addRow = 0;
var sheetName = [];
var rangeObj = [];
//Get last row/column from used range
arryRange.load(["rowCount", "columnCount", "values", "address"]);
await context.sync();
sheets.items.forEach((sheet) => sheetName.push(sheet.name));
var cellAddress, curSheetName;
const mySheets = context.workbook.worksheets;
for (let iRow = 0; iRow < arryRange.rowCount; iRow++) {
curSheetName = arryRange.values[iRow][0]
if (sheetName.indexOf(curSheetName) != -1) {
for (let iCol = 1; iCol < arryRange.columnCount; iCol++) {
cellAddress = arryRange.values[0][iCol];
if (cellAddress) {
const sheet = mySheets.getItem(curSheetName);
const range = sheet.getRange(cellAddress);
range.values = arryRange.values[iRow][iCol];
rangeObj.push(range);
}
}
} else {
// code for higlight Row in db
console.log("Y");
}
}
console.log("Range object created");
await context.sync();
// console.log(arryRange.rowCount);
// console.log(arryRange.columnCount);
console.log("done");
// Copy a range starting at a single cell destination.
});
allRollback = true;
return allRollback;
}
More references:
https://learn.microsoft.com/en-us/office/dev/add-ins/excel/performance?view=excel-js-preview
https://learn.microsoft.com/en-us/office/dev/add-ins/concepts/correlated-objects-pattern
with refer of you assumption, please note that master sheet was crated based on actual workbook with user selected area + deletion of empty value columns in master sheet (I,e empty cell at the adress at each sheet). sheet's name will be as same as actual sheet name unless user don’t change value in master sheet by accident.
With reference to yours,
Suggestions 1) I believe that Move unchanged value out of loops will be the key to my problem. I will refabricate one range and get data comparison for changes. I believe speed will be approved drastically in best case scenario. (I will also have some worst-case scenario (less than 5% case), where I will be required to write every values of master sheet).
Suggestions 2) I am planning to have a new functionality, which may have more rows as address in, that is the reason I have I am keep looking for address rows.
Thanks for your reply.

Google script timeout

I've been using a macro in an Excel for some years and wanted to translate it in a google script to collaborate in Drive.
I'm using a two sheets setup (one named "BILAN" which is the overview and one named INPUT for entering data. The script works just fine while there is not too much inputs, but I'm expecting to reach near a thousand inputs by the end of the file's use.
Basically, the script is a double loop to summarize the inputs in the BILAN sheet. Thanks in advance for your help !!
Here's the code I'm using :
function getTransportDates() {
var ss = SpreadsheetApp.getActive();
var strDatesTransport = '';
const intNbClients = ss.getSheetByName('BILAN').getDataRange().getLastRow();
const intNbInputs = ss.getSheetByName('INPUT').getDataRange().getLastRow();
for (let i = 4; i <= intNbClients; i++) { // loop through the addresses in BILAN
if (ss.getSheetByName('BILAN').getRange(i, 9).getValue() >0) {
for (let j = 4; j <= intNbInputs; j++) { // loop through the adresses in INPUT
if (ss.getSheetByName('INPUT').getRange(j, 2).getValue() == ss.getSheetByName('BILAN').getRange(i, 1).getValue()) {
strDatesTransport = strDatesTransport + ' // ' + ss.getSheetByName('INPUT').getRange(j, 1).getValue(); //.toISOString().split('T')[0];
}
}
}
ss.getSheetByName('BILAN').getRange(i, 10).setValue(strDatesTransport);
strDatesTransport = '';
}
};
Try it this way:
function getTransportDates() {
const ss = SpreadsheetApp.getActive();
var sdt = '';
const csh = ss.getSheetByName('BILAN');
const cvs = csh.getRange(4, 1, csh.getLastRow() - 3, csh.getLastColumn()).getValues();
const ish = ss.getSheetByName('INPUT');
const ivs = ish.getRange(4, 1, ish.getLastRow() - 3, ish.getLastColumn()).getValues();
cvs.forEach((cr,i) => {
if((cr[8] > 0)) {
ivs.forEach((ir,j)=>{
if(ir[1] == cr[0]) {
sdt += ir[0];
}
});
}
ss.getSheetByName('BILAN').getRange(i + 4, 10).setValue(sdt);
sdt = '';
});
}
Don't know where this goes: //.toISOString().split('T')[0];
Reduce the number of calls to get info from Google Sheets
Whenever the interpreter comes to something like this:
ss.getSheetByName('INPUT')
... it has to go to the Google Sheet to see if there is (currently) a sheet of that name, and then has to find the relevant cell within that sheet. Even though the script is running on a Google server, accessing a spreadsheet still takes more time than accessing a variable within the local Javascript environment.
The easiest way to reduce the number of calls is to read each of the sheets ("BILAN" and "INPUT") into a local Javascript variable.
In fact, it looks to me like you are extracting extremely specific sets of cells from each of the spreadsheets. Could you get each set of cells into an array, and then process the arrays?

Optimize for-loop function in Google Apps Script (Arrays maybe)?

First-time poster here. I would like some insight on some Google App Script code i think could be spruced up a bit.
Long story short.
I have a 2 Google Sheet tables
A “LOCALE” spreadsheet - listing unique names of locations
A “FEED” spreadsheet - listing photo descriptions, including the locations. The same location is listed multiple times in this spreadsheet.
Both of these tables have a “Location” column, which references each other with a Key column.
The problem I want to solve for:
When I edit a location name in the “LOCALE” spreadsheet, it should automatically update all the location names in the “FEED” spreadsheet.
The way I solved this problem:
I used a for loop within a for loop for this. To summarize:
for every row in "LOCALE"...
..go through every row in "FEED"...
...If a value in the Key column in the FEED Sheet matches a value in the Key column in the LOCALE Sheet...
...but the value in the Location column in the FEED Sheet doesn't match the value in the Location column in the LOCALE Sheet...
...update the Location column in the FEED Sheet with the value in the Location column in the LOCALE Sheet.
If you're not confused yet, here's the code i wrote for it:
// for each row in the "Locale" sheet...
for(var L = LocationsRefValues.length-1;L>=0;L--) {
// for each row in the "Feed" sheet...
for(var F = FeedRefValues.length-1;F>=0;F--) {
if (FeedRefValues[F][97] == LocationsRefValues[L][17] &&
FeedRefValues[F][10] != LocationsRefValues[L][1]) {
FeedDataSheet.getRange(F+2,10+1).setValue(LocationsRefValues[L][1]);
}
}
}
Now, this code works perfectly fine, I've had no issues. However, i feel like this a bit clunky, as it takes a while to finish its edits. I'm certain there's any easier way to write this and run this code. I've heard arrays may address this situation, but i don't know how to go about that. Hence, why I'm looking for help. Can anyone assist?
Keep in mind I'm a total Google App Script beginner who got this code working through sheer dumb luck, so the simpler the solution the better. Thanks for any consideration to my problem in advance. Looking forward to hearing from you all.
This is the full function (after i made edits suggested here.)
function ModeratorStatus() {
var Data = SpreadsheetApp.getActiveSpreadsheet(); // Local Spreadsheet
var ModeratorStatusDataSheet = Data.getSheetByName("The Status (Moderators)");
var ModeratorStatusRange = ModeratorStatusDataSheet.getRange("A2:C");
var ModeratorStatusRefValues = ModeratorStatusRange.getValues();
var ModeratorDataSheet = Data.getSheetByName("The Moderator_Numbers"); // DATA "Member" sheet
//var ModeratorRefValues = ModeratorDataSheet.getRange("A2:AD").getValues();
var ModeratorStatusObj = {};
for (var MOS = ModeratorStatusRefValues.length-1; MOS>=0; MOS--) {
ModeratorStatusObj[ModeratorStatusRefValues[MOS][2]] = ModeratorStatusRefValues[MOS][0];
}
var ModeratorValues = ModeratorDataSheet.getRange("A1:AD").getValues();
for (var MO = ModeratorValues.length-1; MO >=0; MO--) { // for each row in the "Moderator" sheet...
var ModeratorVal28 = ModeratorValues[MO][28];
if (ModeratorStatusObj[ModeratorVal28] != ModeratorValues[MO][1]) {
ModeratorValues[MO][1] = ModeratorStatusObj[ModeratorVal28];
}
}
var destinationRange = ModeratorDataSheet.getRange(1, 1, ModeratorValues.length, ModeratorValues[0].length);
destinationRange.setValues(ModeratorValues);
I used the code in a different function as a test. To make it easier
LOCALE = MODERATOR STATUS
FEED = MODERATOR
If there are no duplicate [17]s with different [1]s in the LocationsRefValues, you can reduce the computational complexity from O(n ^ 2) to O(n) by creating a mapping object for LocationsRefValues beforehand, whose keys are the LocationsRefValues[L][17]s and whose values are the LocationsRefValues[L][1]s:
var locationObj = {};
for (var L = 0; L < LocationsRefValues.length; L++) {
locationObj[LocationsRefValues[L][17]] = LocationsRefValues[L][1];
}
for (var F = FeedRefValues.length - 1; F >= 0; F--) { // for each row in the "Feed" sheet...
var feedVal97 = FeedRefValues[F][97];
if (locationObj[feedVal97] != FeedRefValues[F][10]) {
FeedDataSheet.getRange(F + 2, 10 + 1).setValue(locationObj[feedVal97]);
}
}
Thanks #TheMaster, you can speed this up by calling setValue only once, at the end, rather than calling it in a loop, probably something along the lines of:
var locationObj = {};
for (var L = 0; L < LocationsRefValues.length; L++) {
locationObj[LocationsRefValues[L][17]] = LocationsRefValues[L][1];
}
var feedValues = FeedDataSheet.getValues();
for (var F = FeedRefValues.length - 1; F >= 0; F--) { // for each row in the "Feed" sheet...
var feedVal97 = FeedRefValues[F][97];
if (locationObj[feedVal97] != FeedRefValues[F][10]) {
feedValues[F + 2][10 + 1] = locationObj[feedVal97];
}
}
var destinationRange = ss.getRange(1, 1, feedValues.length, feedValues[0].length);
destinationRange.setValues(feedValues);
you can use onEdit(e) trigger to get the reference to the edited cell. In that case you won't need to iterate over the entire Locale" sheet:
function onEdit(e) {
var range = e.range; // edited cell
var rowIndex = range.getRow()
var colIndex = range.getColumn()
if (rowIndex >= LocaleRange.startRow && rowIndex <= LocaleRange.EndRow &&
colIndex >= LocaleRange.startColumn && colIndex <= LocaleRange.EndColumn) {
var index = rowIndex - LocaleRange.startRow
var keyValue = LocationsRefValues[index][17]
var newLocValue = range.getValue()
var newFeedValues = FeedRefValues.map(function (row) {
return (row[97] == keyValue) newLocValue ? : row[10]
})
FeedDataRange.setValues(newFeedValues)
}
}
Here are docs on using onEdit trigger: https://developers.google.com/apps-script/guides/triggers

Trying to do essentially a vlookup in an AdWords Script but it takes too long

I'm trying to match the location input (which may be a post code or a neighborhood) to the AdWords location ID. I have a massive google sheet with almost 100,000 rows with a list of all the post codes, cities, neighborhoods, etc. and their corresponding IDs, and I'm trying to write a function where you can input post code (for example) and it gives you the location ID. So far I have:
var locationSpread = SpreadsheetApp.openById('scriptID');
var locationSheet = locationSpread.getSheetByName('Sheet1');
var locationRow = locationSheet.getLastRow();
var locationMatch = function(locationInput) {
for (var i = 2; i <= locationRow; i++) {
var locationName = locationSheet.getRange([i], 2).getValue();
if (locationName == locationInput) {
return locationSheet.getRange([i], 1);
}
}
}
Logger.log(locationMatch('DD7'));
I think that in theory this should work, but the limit for AdWords scripts is 30 minutes and since this has to iterate through so many rows it simply isn't feasible, it's taking over 20 minutes to return the example I'm logging, and the script should be doing this dynamically for a bunch of inputs.
Is there a faster/better way of doing this? Or is there an issue with the script itself that's causing it to run so slowly?
Using .getValues() might be faster. You'd need to try it out.
var locationSpread = SpreadsheetApp.openById('asdf');
var locationSheet = locationSpread.getSheetByName('asdf');
var locationRow = locationSheet.getLastRow();
Logger.log("Starting!")
Logger.log("LocationRow: "+locationRow);
function locationMatch(locationInput) {
var locationNames = locationSheet.getRange(1, 2,locationRow,2).getValues();
for (var i = 1; i <= locationRow-1; i++) {
Logger.log("locationNames[i][0]:"+locationNames[i][0])
if (locationNames[i][0] == locationInput) {
return locationSheet.getRange([i], 1);
}
}
}
Logger.log("locationMatch Result:"+locationMatch('MA'));

How to use a function output as a variable in GAS and not run the function each time?

I'm new to Javascript/Coding in general and am trying to solve a problem.
I have a program that pulls all my contact emails into an array then sorts through them and adds new emails. Pulling all the contacts takes too long (2000 contacts!) and I want to have Google Apps Script run this part automatically every 5 minutes to I have an updated list if I want to run the sort function.
Is it possible to make the contact pulling part of my function it's own function then use logger.log to save the values for use later? How can a seperate function access the logged info from this new function?
function getEmailAddressList(){
var addrA = [];
var contact = ContactsApp.getContacts();
//////////
for(var i = 0; i < contact.length; i++){
var addresses = contact[i].getEmails();
for(var j = 0;j < addresses.length; j++){
var address = addresses[j].getAddress();
addrA.push(address);
logger.log(addrA);
};
};
Depending upon the size of your data, you can store the data in "Properties Service" or "Cache." Properties Service can hold 9k of data per property name and a total of 500k. If the data will ever be larger than that, then you will need a different data storage option. For example, you could store the contact information in a text file in your Google Drive. You could obviously save your data to a database also. The point is, you need to store the contact information somewhere. You can not store it in a global variable, because a global variable will loose it's data as soon as the code stops running. Logger.log() won't help you in this situation.
You can run code on a time interval with a time based trigger. You will need to set that up from the "Edit" menu and "Current project's triggers."
Here is an example of how you might store the data in a text file:
function getEmailAddressList(){
var addrA,addresses,addressFile,allContacts,
contactsAsJSON,i,j,thisAddress,thisContact;
allContacts = ContactsApp.getContacts();
//////////
addrA = [];
for (i = 0; i < allContacts.length; i++) {
addresses = allContacts[i].getEmails();
for (j = 0;j < addresses.length; j++) {
thisAddress = addresses[j].getAddress();
thisContact = addresses[j].getDisplayName();
addrA.push(thisAddress);
Logger.log(thisContact);
}
}
contactsAsJSON = JSON.stringify(addrA);
try{
addressFile = DriveApp.getFileById('id');
}catch(e){}
if (!addressFile) {
addressFile = DriveApp.createFile('My Contacts', contactsAsJSON);
Logger.log('New file ID: ' + addressFile.getId());//Get the new file ID and hard code into
//the code above
}
addressFile.setContent(contactsAsJSON);
};
Here is an example of how you might store all contacts in Cache Service with each contact being one property name:
function getEmailAddressList(){
var addresses,allContacts,Cache,i,j,thisAddress,thisContact;
Cache = CacheService.getDocumentCache();
allContacts = ContactsApp.getContacts();
//////////
for (i = 0; i < allContacts.length; i++) {
addresses = contact[i].getEmails();
for (j = 0;j < addresses.length; j++) {
thisAddress = addresses[j].getAddress();
thisContact = addresses[j].getDisplayName();
Cache.put(thisContact, thisAddress)
logger.log(thisContact);
}
}
};

Categories

Resources