I'm starting to learn about google app script with JavaScript.
I wrote this code, but it is really slow and executing the function takes too long. I think this isn't the best way to do the function.
Is there any option/way (array), to make it faster or in other way to reduce to the time waiting?
All I wish to do is to insert "today's date", when a column cell value is set true, in another cell column.
function onEdit() {
sConfDate();
}
function sConfDate(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var dataSheet = ss.getSheetByName("Data");
var dataRange = dataSheet.getRange(9, 1, dataSheet.getLastRow(), 43);
var data = dataRange.getValues();
var d = new Date();
var dd = d.getDate();
var mm = d.getMonth() +1;
var yyyy = d.getFullYear();
var date = dd + "/" + mm + "/" + yyyy;
var needSay = 'SUC';
var needSay2 = '-';
for(var i = 0; i < data.length; i++){
//row items
var values = data[i];
//column items
for(var j = 0; j < values.length; j++){
var row = values[j];
var checkRow = row;
//Logger.log(checkRow);
if(checkRow === needSay && checkRow === needSay2){
dataSheet.getRange(9 + i, 43).setValue(date);
};
};
};
}
Perhaps this is what you had in mind:
function sConfDate(){
var ss=SpreadsheetApp.getActive();
var dataSheet=ss.getSheetByName("Data");
var dataRange=dataSheet.getRange(9, 1, dataSheet.getLastRow()-8, 43);
var data=dataRange.getValues();
var datacol43=dataSheet.getRange(9,43,dataSheet.getLastRow()-8,1).getValues();
var date=Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy");
data.forEach(function(r,i){
r.forEach(function(c,j){
if(c=='SUC' || c=='-') {
datacol43[i][0]=date;
}
});
});
dataSheet.getRange(9,43,dataSheet.getLastRow()-8,1).setValues(datacol43);
}
This might actually run faster:
function sConfDate(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("Data");
var rg=sh.getRange(9, 1, sh.getLastRow()-8, 43);
var data=rg.getValues();
var crg=sh.getRange(9,43,sh.getLastRow()-8,1);
var cdata=crg.getValues();
var date=Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy");
for(var i=0;i<data.length;i++) {
for(var j=0;j<data[i].length;j++) {
if(data[i][j]=='SUC' || data[i][j]=='-') {
cdata[i][0]=date;
break;
}
}
}
crg.setValues(cdata);
}
Perhaps this would be better.
function sConfDate2(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName("Data");
var rg=sh.getRange(9, 1, sh.getLastRow()-8, 8);
var data=rg.getValues();
var crg=sh.getRange(9,8,sh.getLastRow()-8,1);
var cdata=crg.getValues();
var date=Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "dd/MM/yyyy");
for(var i=0;i<data.length;i++) {
var row=data[i];//Im guessing this is probably what you want unless you have particular columns in mind
if(row.indexOf('SUC')!=-1 && row.indexOf('-')!=-1) {
cdata[i][0]=date;
}
}
crg.setValues(cdata);
}
Related
Test1 Sheet
Test2 Sheet
I want to compare News01 from Test1 A Column with Test2 A Column, and need to fetch the corresponding Test2 B column value
So the result should be Finance in Sheet1 B column
But If I use else, even when if statement is true it's going to else statement.
If I delete else statement, then if statement is working.
I don't know why when if statement is true, it's going to else statement by default
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
break;
}
else{
Logger.log("News not found")
}
}
});
}
You are logging News not found for each comparisons with all the rows of Test2, which is improper.
Instead, you should only log when the comparison is finished.
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
let found = false;
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
found = true;
break;
}
}
if (!found) { Logger.log("News not found"); }
});
}
Or simply,
function test() {
var sheet = SpreadsheetApp.getActiveSpreadsheet();
var source_sheet = sheet.getSheetByName("Test1");
var target_sheet = sheet.getSheetByName("Test2");
var lastRow = source_sheet.getLastRow();
var inputs = source_sheet.getRange('A' + lastRow).getValues().flat();
var days = target_sheet.getRange('A1:A').getValues().flat();
var codes = target_sheet.getRange('B1:B').getValues().flat();
inputs.forEach(function(input, count){
for(var i = 0; i < days.length; i++){
if(days[i].trim() == input.trim()){
source_sheet.getRange('B' + (count + source_sheet.getLastRow())).setValue(codes[i]);
Logger.log(codes[i]);
return;
}
}
Logger.log("News not found");
});
}
function test() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh1 = ss.getSheetByName("Sheet0");
var sh2 = ss.getSheetByName("Sheet1");
var lastRow = sh1.getLastRow();
var vs1 = sh1.getRange(1,1,sh1.getLastRow()).getValues().flat().map(e => e.trim());
var vs2 = sh2.getRange(1,1,sh2.getLastRow(),2).getValues();
vs2.forEach(r => {
if(~vs1.indexOf(r[0].trim())) {
Logger.log(r[1]);
}
});
}
I am coding a room booking system using combination of Google forms and Google calendar.
When there is a new booking order:
An event will be automatically created on the selected calendar.
An edit response URL will also be generated automatically and put in column 10 of the spreadsheet in the same row where the form answer was inserted.
// This is the function to generate the edit URL (which works perfectly).
function getEditUrl(request) {
var formRes = FormApp.openById('XXXXXXXXXXXXXXXXXXXXXXXXXXXX');
var sheetRes = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('YYYYYYYYYY');
var data = sheetRes.getDataRange().getValues();
var urlCol = 10;
var responses = formRes.getResponses();
var timestamps = [],
urls = [],
resultUrls = [];
for (var i = 0; i < responses.length; i++) {
timestamps.push(responses[i].getTimestamp().setMilliseconds(0));
urls.push(responses[i].getEditResponseUrl());
}
for (var j = 1; j < data.length; j++) {
resultUrls.push([data[j][0] ? urls[timestamps.indexOf(data[j][0].setMilliseconds(0))] : '']);
}
sheetRes.getRange(2, urlCol, resultUrls.length).setValues(resultUrls);
};
However, problem occurs when there are more than 2 orders; as the next order will delete the calendar event from the previous order.
// This is the function to update the calendar event.
function updateCalendar(request) {
var sheet = SpreadsheetApp.getActiveSheet();
var lastRow = sheet.getLastRow();
var range = sheet.getRange(2, 1, lastRow, 13);
var values = range.getDisplayValues();
var calendar = CalendarApp.getCalendarById('XXXXXXXXXXXXXXXXXXXXXXXX#group.calendar.google.com');
for (var i = 0; i < responses.length; i++) {
getConflicts(request);
if (request.eventConflict == "conflict") {
sheet.getRange(lastRow, 11).setValue("conflict");
break;
} else if (request.eventConflict == "approve") {
var newEvent = calendar.createEvent("booked", request.date, request.endTime);
var newEventId = newEvent.getId().split('#')[0];
sheet.getRange(lastRow, 11).setValue("approve");
sheet.getRange(lastRow, 12).setValue(newEventId);
break;
}
}
for (var j = 1; j < values.length; j++) {
if (values[j][10] == "approve") {
var eventEditId = calendar.getEventSeriesById(values[j][11]);
eventEditId.deleteEventSeries();
sheet.getRange(j + 2, 11).setValue("");
getConflicts(request);
if (request.eventConflict == "approve" && values[j][10].length > 1) {
var newEvent = calendar.createEvent("booked", request.date, request.endTime);
var newEventId = newEvent.getId().split('#')[0];
sheet.getRange(j + 2, 11).setValue("approve");
sheet.getRange(j + 2, 12).setValue(newEventId);
break;
} else {
sheet.getRange(j + 2, 11).setValue("conflict");
break;
}
}
}
};
My questions:
How to make sure that when respondent edits his/her own response, it will always update event from the same column as the edit URL? --> I have separate function that will send edit URL to respondents
When there is more than two submission, the 3rd submission will delete event of the 2nd one. (I am sure the issue is on the updateCalendar() function).
I have been struggling so much for the past few days trying to figure out the best way to come up with best loop method. Any help / response is greatly appreciated.
EDIT:
This is the column description of the sheets (separated with |):
Timestamp
Email Address
name
Check-in date
Check-out date
Room
No. of people
total day
total
edit URL
Event Conflict
Event ID
This is the function to get event conflicts in the calendar:
function getConflicts(request){
var conflicts = request.calendar.getEvents(request.date, request.endTime);
if (conflicts.length > 0) {
request.eventConflict = "conflict";
} else {
request.eventConflict = "approve"
}
};
And this is the main function that will be triggered on formsubmit:
function main(){
var request = new Submission(lastRow);
getEndTime(request);
draftEmail(request);
updateCalendar(request);
};
This is the screenshot of the sheet
Finally I found one way to retrieve the edited row by using e.range method. So basically I created another sheet inside the same spreadsheet. When there is a new submission, it will automatically copy the new submission to the second sheet. And when there is an edited submission, it will go through the copy sheet to find the edited row, and then edit it (as well as the calendar). Credit to Tedinoz
function updateCalendarTwo(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var responsename = "AAAAAAAAAAAAAAA"
var copyname = "BBBBBBBBBBBB";
var responsesheet = ss.getSheetByName(responsename);
var copysheet = ss.getSheetByName(copyname);
var calendar = CalendarApp.getCalendarById('CCCCCCCCCCCCCCCCCCCC');
// columns on copysheet
var checkInCol = 4;
var checkOutCol = 5;
var roomNumCol = 6;
var appCol = 11
var eventIDCol = 12;
var revCol = 14;
var response = e.range;
var rRow = response.getRow()
var rLC = responsesheet.getLastColumn();
var cLC = copysheet.getLastColumn();
var rLR = responsesheet.getLastRow();
var cLR = copysheet.getLastRow();
if (rLR > cLR){
var resprange = responsesheet.getRange(rLR,1,1,rLC);
var respdata = resprange.getValues();
copysheet.appendRow(respdata[0]);
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var event = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var eventID = event.getId().split('#')[0];
copysheet.getRange(rRow,appCol).setValue("approve");
copysheet.getRange(rRow,eventIDCol).setValue(eventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
}
} else {
var resprange = responsesheet.getRange(rRow,1,1,9);
var respdata = resprange.getValues();
var copyrespRange = copysheet.getRange(rRow,1,1,9);
copyrespRange.setValues(respdata);
var respAppRange = copysheet.getRange(rRow,appCol);
var respApp = respAppRange.getValue();
if (respApp == 'conflict') {
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
} else {
var eventEditId = copysheet.getRange(rRow,eventIDCol).getDisplayValue();
var editedEvent = calendar.getEventSeriesById(eventEditId);
editedEvent.deleteEventSeries();
var eventTitle = copysheet.getRange(rRow,roomNumCol).getValue();
var startDate = copysheet.getRange(rRow,checkInCol).getValue();
var endDate = copysheet.getRange(rRow,checkOutCol).getValue().getTime()+ 24 * 60 * 60 * 1000;
var conflicts = calendar.getEvents(new Date(startDate), new Date(endDate));
if (conflicts.length < 1) {
var editedEvent = calendar.createAllDayEvent(eventTitle, new Date(startDate), new Date(endDate));
var editedEventID = editedEvent.getId().split('#')[0];;
copysheet.getRange(rRow,appCol).setValue("edited");
copysheet.getRange(rRow,eventIDCol).setValue(editedEventID);
} else {
copysheet.getRange(rRow,appCol).setValue("conflict");
};
};
var revRange = copysheet.getRange(rRow,revCol);
var revOldValue = revRange.getValue();
if (revOldValue == null || revOldValue == ""){
revOldValue = 0;
}
var revNewValue = revOldValue+1;
revRange.setValue(revNewValue);
}
}
I want to get sums of numeric values for each separate column in the range. What I do wrong?
function countNutrients(){
var sh = SpreadsheetApp.getActiveSheet();
var lastcol = sh.getLastColumn(); //Get last column
var lastcolval = sh.getRange(1, 1, 1, lastcol).getValues();
var lastrowval = sh.getRange("C1:C").getValues(); //Trick to get last row
var lastrow = lastrowval.filter(String).length; //
var sumvalues = sh.getRange(2, 14, lastrow, lastcol).getValues();
(typeof sumvalues === 'number' ? "" : sumvalues);
Logger.log(sumvalues);
for (var j = 13; j < lastcolval.length ; j++) {
var sumcolumnval = sh.getRange(2, [j], lastrow);
var sum = 0;
for (var i in sumcolumnval[0]) {
var sum = sumvalues[0][i] + sum;
}
return sum
Logger.log(sum);
}
return sumcolumnval
}
Columns on the screenshot and 65 more
enter link description here
CSV example
Sum of Columns With a Script
function sumOfCols() {
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet14');
var hA=sh.getRange(1,1,1,sh.getLastColumn()).getValues()[0];
var sum={};
hA.forEach(function(h,i){sum[h]=0;});
var rg=sh.getRange(2,1,sh.getLastRow()-1,sh.getLastColumn());
var v=rg.getValues();
v.forEach(function(r,i){r.forEach(function(c,j){sum[hA[j]]+=c;});});
var html='';
hA.forEach(function(h,i){html+=Utilities.formatString('<br />%s(sum): %s',h,sum[h]);});
var ui=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(ui, "Column Sums")
}
Spreadsheet:
Output Dialog:
Data(csv):
COL1,COL2,COL3,COL4,COL5,COL6,COL7,COL8,COL9,COL10
1,2,3,4,5,6,7,8,9,10
2,3,4,5,6,7,8,9,10,11
3,4,5,6,7,8,9,10,11,12
4,5,6,7,8,9,10,11,12,13
5,6,7,8,9,10,11,12,13,14
6,7,8,9,10,11,12,13,14,15
7,8,9,10,11,12,13,14,15,16
A function for your particular Application
This will sum just the six columns that you selected
function countNutrients(){
var sh=SpreadsheetApp.getActiveSheet();
var hA=sh.getRange(1,14,1,6).getValues()[0];
var sum={};
hA.forEach(function(h,i){sum[h]=0;})
var vA=sh.getRange(2,14,sh.getLastRow()-1,6).getValues();
vA.forEach(function(r,i){
r.forEach(function(c,j){
if(!isNaN(c)) {
sum[hA[j]]+=c;
}
});
});
var html="";
var keys=Object.keys(sum);
keys.forEach(function(k,i){
html+=Utilities.formatString('<br />sum[%s]=%s',k,sum[k]);
})
var ui=HtmlService.createHtmlOutput(html);
SpreadsheetApp.getUi().showModelessDialog(ui,'Sums of Cols N through S')
}
I have this piece of script.
It filter a range by a criteria,
then It copy values that respect criteria in a specific sheet
then It deletes all the row in the original sheet that respect the criteria.
So that If my range contains more than 1000 rows, It's said to me error: Google app script timeout.
I put my code here, can You help me to get a better performance about execution time of this script?
function trasferisciDati() {
var ui = SpreadsheetApp.getUi();
var response = ui.prompt('Inserisci il mese dei dati da esportare', '(Esempio: agosto (tutto minuscolo))', ui.ButtonSet.OK_CANCEL);
var inizioTRASFERISCIVALORI = Utilities.formatDate(new Date(), "CET", "HH:mm:ss.SSS");
if (response.getSelectedButton() == ui.Button.OK) {
//get filtered range and set values to the new range
var description = ui.prompt('Inserisci una descrizione per questa esportazione', 'apparirà come tag dell\'esportrazione', ui.ButtonSet.OK_CANCEL);
var sourceData = SpreadsheetApp.openById("1XkYhjdQfgU7mVCR7E8mfZsf292I-cJ16PnpCimnd1v8").getSheetByName("Prova");
var destinationData = SpreadsheetApp.openById("1cdXMqqBwgWK5nCQUtAP_TyIIDOHksS7wWvSG4jRu658").getSheetByName("Prova");
var lastRow = sourceData.getLastRow();
var data = sourceData.getRange(1, 1, lastRow, 1).getValues();
var chiave = response.getResponseText();
for(var i=0;i<data.length;i++)
{
if (data[i][0] == chiave) {
var filteredRow = sourceData.getRange(i+1,1,1,5).getValues();
destinationData.appendRow(filteredRow[0]);
}
}
//number of records of the filtered range
var lastRow = destinationData.getLastRow();
var data = destinationData.getRange(1, 6, lastRow, 1).getValues();
var loop = 0
for(var i=0;i<data.length;i++)
{
if(!data[i][0])
{
var loop = loop + 1
}
}
Logger.log(Utilities.formatString('%1.0f', Math.floor(loop)))
//appendi timestamp al rigo ed eventuale descrizione aggiuntiva inserita dall'utente
var lastRow = destinationData.getLastRow();
var data = destinationData.getRange(1, 6, lastRow, 1).getValues();
var timestamp = Utilities.formatDate(new Date(), "CET", "dd/MM/YYYY HH.mm.ss")
for(var i=0;i<data.length;i++)
{
if(!data[i][0])
{
destinationData.getRange(i+1,6).setValue(timestamp)
destinationData.getRange(i+1,7).setValue(description.getResponseText())
}
}
//cancella l'intervallo originale
var maxRows = sourceData.getMaxRows();
var data = sourceData.getRange(1, 1, maxRows, 1).getValues();
for(var i=data.length; i>=0;i--)
{
if (data[i] == chiave) {
sourceData.deleteRow(i+1)
}
}
var fineTRASFERISCIVALORI = Utilities.formatDate(new Date(), "CET", "HH:mm:ss.SSS");
var inTime=inizioTRASFERISCIVALORI.split(":");
var outTime= fineTRASFERISCIVALORI.split(":");
var hr = outTime[0] - inTime[0];
var min = ((outTime[1] - inTime[1])+hr*60)%60;
var sec = ((outTime[2] - inTime[2])+min*60)%60;
var duration = Utilities.formatString('%2.0f', Math.floor(hr)) + 'h ' + Utilities.formatString('%2.0f', Math.floor(min)) + 'm ' + Utilities.formatString('%2.0f', sec) + 's';
ui.alert('Trasferite '+ Utilities.formatString('%1.0f', Math.floor(loop)) +' righe in '+ duration, ui.ButtonSet.OK)
} else if (response.getSelectedButton() == ui.Button.CANCEL) {
SpreadsheetApp.getUi().alert('L\'utente ha annullato l\'operazione');
} else {
SpreadsheetApp.getUi().alert('L\'utente ha chiuso la finestra di dialogo');
}
}
You might try this:
var data = sourceData.getRange(1, 1, lastRow, 5).getValues();
var chiave = response.getResponseText();
for(var i=0;i<data.length;i++)
{
if (data[i][0] == chiave)
{
//var filteredRow = sourceData.getRange(i+1,1,1,5).getValues();
destinationData.appendRow(data[i]);
}
}
And you might consider the same thing on your destination data.
Well, here is the json file http://herbalista.hol.es/group.json i am working with JSON.parse(); on Google apps script. I temporarily solve with this code by Choosing the post which have more than 15 likes, but i want to choose the one with more likes independently if have or not more than 15 likes.
function repost() {
var UsrAccess_token = "xxxxxxxxx"
var graph = "https://graph.facebook.com/xxxxxx/feed/?access_token="+UsrAccess_token+"";
var jsondata = UrlFetchApp.fetch(graph,{method:"get"}).getContentText();
var object = JSON.parse(jsondata);
var item = object.data;
var currentTime = new Date();
var year = currentTime.getUTCFullYear();
var month = (currentTime.getUTCMonth()) + 1;
var day = (currentTime.getUTCDate()) - 1;
if (day <= 9) {var day = "0"+day+"";}
if (month <= 9) {var month = "0"+month+"";}
var utime = ""+year+"-"+month+"-"+day+"T";
try {
var i = null;
for (i = 0; item.length > i; i += 1) {
var pubDate = item[i].created_time;
if (pubDate.match(utime)) { var likesdata = item[i].likes.data; var len = likesdata.length;
if (len > 15) {var popular = item[i].link;}}
}} catch(err) {
var err = "ERROR";
}
}
For this you can Choose a default value for a variable like var maxLikes = 0; and verify against len variable.
The code would be something like this:
function repost() {
var UsrAccess_token = "xxxxxxxxx"
var graph = "https://graph.facebook.com/xxxxxx/feed/?access_token="+UsrAccess_token+"";
var jsondata = UrlFetchApp.fetch(graph,{method:"get"}).getContentText();
var object = JSON.parse(jsondata);
var item = object.data;
var currentTime = new Date();
var year = currentTime.getUTCFullYear();
var month = (currentTime.getUTCMonth()) + 1;
var day = (currentTime.getUTCDate()) - 1;
if (day <= 9) {var day = "0"+day+"";}
if (month <= 9) {var month = "0"+month+"";}
var utime = ""+year+"-"+month+"-"+day+"T";
try {
var i = null;
var maxLikes = 0;
for (i = 0; item.length > i; i += 1) {
var pubDate = item[i].created_time;
if (pubDate.match(utime)) {
var likesdata = item[i].likes.data;
var len = likesdata.length;
if (len > maxLikes) {
maxLikes = len;
var popular = item[i].link;
}
}
}
} catch(err) {
var err = "ERROR";
}
}
Hope that helps!