'Apps Script For within For Faster
I used the below code to get the data from API. I used for loop within for loop and it's taking long time and program stops as time exceeds.
function devicedetails(){
var apikey='YWQ0OWFhYjgtNTY2asiHSNSajiasn'
var todaydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24), "UTC", "yyyy-MM-dd")
var thirtydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24*30), "UTC", "yyyy-MM-dd")
var cisss= SpreadsheetApp.getActiveSpreadsheet();
var workspacesheet = cisss.getSheetByName("Device");
var lastRows = workspacesheet.getLastRow()+1;
for(var im = 2; im < lastRows; im++)
{
var workspacedata = workspacesheet.getRange('B'+im).getValue();
var encodedata = encodeURIComponent(workspacedata);
var cisurl = "https://testapi.com/v1/workspaceDurationMetrics?workspaceId="+encodedata+"&aggregation=daily&measurement=timeUsed&from="+thirtydate+"T00%3A00%3A00.00Z&to="+todaydate+"T00%3A00%3A00.00Z";
var cisss= SpreadsheetApp.getActiveSpreadsheet()
var ciswsLocation = cisss.getSheetByName("HourlyUsed")
var lastRow = ciswsLocation.getLastRow();
var headers = {
"Content-type": "application/json",
"Authorization": `Bearer ${apikey} `
};
var options = {
"method" : "get",
"headers" : headers
};
var response = UrlFetchApp.fetch(cisurl,options);
var cisjson=response.getContentText();
var cisdata=JSON.parse(cisjson);
for(var i = 0; i < cisdata['items'].length; i++)
{
ciswsLocation.getRange(lastRow+1+i,1).setValue([cisdata["workspaceId"]]);
ciswsLocation.getRange(lastRow+1+i,2).setValue(Utilities.formatDate(new Date([cisdata["items"][i]['start']]), "UTC", "yyyy-MM-dd"));
ciswsLocation.getRange(lastRow+1+i,3).setValue([cisdata["items"][i]['duration']]);
}
}
}
Please help me how to reduce time of execution?
Solution 1:[1]
Exactly what liqidkat said.
With that, it may look something like this:
function devicedetails() {
/** Variables **/
const apikey ='YWQ0OWFhYjgtNTY2asiHSNSajiasn'
const todaydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24), "UTC", "yyyy-MM-dd")
const thirtydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24*30), "UTC", "yyyy-MM-dd")
/** Sheet Variables **/
const cisss = SpreadsheetApp.getActiveSpreadsheet()
const workspacesheet = cisss.getSheetByName("Device")
const workspaceData = workspacesheet.getRange(2, 2, workspacesheet.getLastRow()-1).getValues().flat()
const ciswsLocation = cisss.getSheetByName("HourlyUsed")
const lastRow = ciswsLocation.getLastRow()
/** Request Handling **/
const allRequests = workspaceData.map(i => {
const encodeData = encodeURIComponent(i)
return {
"url": `https://testapi.com/v1/workspaceDurationMetrics?workspaceId=${encodeData}&aggregation=daily&measurement=timeUsed&from=${thirtydate}T00%3A00%3A00.00Z&to=${todaydate}T00%3A00%3A00.00Z`,
"method": "get",
"headers": {
"Content-type": "application/json",
"Authorization": `Bearer ${apikey}`
}
}
})
/** Response Handling **/
const allResponses = UrlFetchApp.fetchAll(allRequests)
const data = allResponses.map(response => {
const cisjson = response.getContentText()
const cisData = JSON.parse(cisjson)
return cisData[`items`].map(i => [
cisdata["workspaceId"],
Utilities.formatDate(new Date(i['start']), "UTC", "yyyy-MM-dd"),
i['duration']
])
})
/** Set data **/
ciswsLocation.getRange(lastRow+1, 3, data.length, data[0].length).setValues(data)
}
See Also:
Solution 2:[2]
I will provide my particular approach to this problem, as I think it may be of interest to the community.
Since the OP has not provided the type of response the API provides (and refers that it is for private use), I will use a public API for the example, Google Books APIs in this case. I will also consider that the calls are made to the same API, so the response is assumed to be identical.
I think the problem can be divided into 4 major steps.
- Generate the URLs of the calls (depends on the API).
- Get the data via
UrlFetchApp.fetchAll(Object) - Normalize the data (this is the most critical step, as it depends on the API response). The main point is to obtain an array of arrays (
Object[][]) as required for the next step. - Write the data to the sheet using
setValues(Object[][]).
Full example here.
Generate URLs
const generateUrl = (authors) => authors.map(author => `https://books.googleapis.com/books/v1/volumes?q=${author}&country=US`)
Get The Data
const fetchAndNormalizeData = (urlList) => {
const resAll = UrlFetchApp.fetchAll(urlList).map(res => res.getContentText())
return resAll.map(normalizeResponse).flat()
}
Normalize The Data
const normalizeResponse = (res) => {
/* This depends on the RestAPI response */
const { items } = JSON.parse(res)
return items.map((book) => {
const { selfLink, volumeInfo: { title, authors, publishedDate } } = book
const parsedAuthors = authors ? authors.join('\n') : ""
return [title, parsedAuthors, selfLink, publishedDate]
})
}
Write to Sheet
const writeToSheet = (data) => {
sS
.getRange(sS.getLastRow() + 1, 1, data.length, data[0].length)
.setValues(data)
console.log("DATA SAVED")
}
Main function
const SS_ID = "<SS_ID>"
const sS = SpreadsheetApp.openById(SS_ID).getSheetByName('BookData')
const main = () => {
const urlList = generateUrl(["Twain", "Joyce", "Pasternak"])
const data = fetchAndNormalizeData(urlList)
writeToSheet(data)
}
In the case of the OP just have to modify the normalizeResponse (callback for the map function) and generateUrl to adapt it to their needs.
Documentation:
Solution 3:[3]
Description
I took the liberty of editing your script to replace all getValue/setValue with getValues/setValues. And I moved all variable that only need to be set once outside the loop. First I get all workspacedata, then in side the loop, index into that array for each row. Next since your results are contiguous in rows and columns, I collect all the results and make one call to setValues to place in the sheet.
Although I am not able to test it since input data is not available I believe it will work and will run much faster.
Although Google has made improvements in it performance of getValue/setValue by caching requests I try to organize my spreadsheets so that I will always use getValues/setValues. Same for other getters and setters.
Script
function devicedetails(){
var apikey='YWQ0OWFhYjgtNTY2asiHSNSajiasn'
var todaydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24), "UTC", "yyyy-MM-dd")
var thirtydate = Utilities.formatDate(new Date(Date.now() - 1000*60*60*24*30), "UTC", "yyyy-MM-dd")
var cisss= SpreadsheetApp.getActiveSpreadsheet();
var workspacesheet = cisss.getSheetByName("Device");
var lastRows = workspacesheet.getLastRow()-1;
var workspacedata = workspacesheet.getRange(2,2,lastRows-1,1).getValues();
var ciswsLocation = cisss.getSheetByName("HourlyUsed")
for(var im = 0; im < lastRows; im++) {
var encodedata = encodeURIComponent(workspacedata[im][0]);
var cisurl = "https://testapi.com/v1/workspaceDurationMetrics?workspaceId="+encodedata+"&aggregation=daily&measurement=timeUsed&from="+thirtydate+"T00%3A00%3A00.00Z&to="+todaydate+"T00%3A00%3A00.00Z";
var lastRow = ciswsLocation.getLastRow();
var headers = {
"Content-type": "application/json",
"Authorization": `Bearer ${apikey} `
};
var options = {
"method" : "get",
"headers" : headers
};
var response = UrlFetchApp.fetch(cisurl,options);
var cisjson=response.getContentText();
var cisdata=JSON.parse(cisjson);
var results = [];
for(var i = 0; i < cisdata['items'].length; i++) {
let row = []
row[0] = cisdata["workspaceId"];
row[1] = Utilities.formatDate(new Date(cisdata["items"][i]['start']), "UTC", "yyyy-MM-dd");
row[2] = cisdata["items"][i]['duration'];
results.push(row);
}
ciswsLocation.getRange(lastRow+1,1,results.length,results[0].length).setValues(results);
}
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | |
| Solution 2 | Emel |
| Solution 3 |
