Google spreadsheet - script throws "Exceeded maximum execution time" error - google-apps-script

I have the following google script that will run against 500+ rows of data in the "InputFromHospital" sheet and in the middle of execution, getting the "Exceeded maximum execution time" error.
I was having few more lines of code but after reading the similar questions in StackOverflow, I removed and kept only the needed lines/source code. I am not sure how I could further optimize.
Looking for your suggestions/expertise to optimize and to reduce the execution time to under 5 minutes?
function feedToMasterAndPolice() {
var ssbook = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ssbook.getSheetByName('MasterPatientData')
var sLastRow = srcSheet.getLastRow() + 1
var inputSheet = ssbook.getSheetByName('InputFromHospital')
var iLastRow = inputSheet.getLastRow();
var pSheet = ssbook.getSheetByName('DataToPolice')
var pLastRow = pSheet.getLastRow() + 1;
var todayDate = Utilities.formatDate(new Date(), "IST", "dd/MMM/yyyy")
//Loop thru all rows in "Input.." sheet
for (var i = 2; i <= iLastRow; i++) {
//Reading contact number from "Input.." sheet
var value = inputSheet.getRange(i, 5).getValue();
var gID = "";
if (value.toString.length > 0) {
//Generating unique patient ID for each COVID Patient
gID = "PID".concat(srcSheet.getLastRow());
srcSheet.getRange(sLastRow, 1).setValue(gID.toString());
//Looping thru all 8 colums in "Input" sheet
for (var colN = 2; colN < 9; colN++) {
var actCellVal = inputSheet.getRange(i, colN).getValue();
//Set value for contact# and date values
if (colN == 5 || colN == 6 || colN == 8) {
srcSheet.getRange(sLastRow, colN).setValue(actCellVal)
} else {
//All String values - upper case
srcSheet.getRange(sLastRow, colN).setValue(actCellVal.toString().toUpperCase())
}
}
var cR = srcSheet.getLastRow();
//Adding formula to calculate "DAYS SINCE ADMISSION" - example =if(A2<>"",TODAY()-F2,"")
srcSheet.getRange(sLastRow, 9).setFormula("=if(A" + cR + "<>\"\",TODAY()-F" + cR + ",\"\")")
//Adding formula to calculate "FOLLOW UP NEEDED" - example =if(I2<=Admin!$C$2,"YES","NO"
srcSheet.getRange(sLastRow, 10).setFormula("=if(I" + cR + "<=\'Admin\'!$C$2,\"YES\",\"NO\")");
//Add current date
srcSheet.getRange(sLastRow, 11).setValue(todayDate);
sLastRow = sLastRow + 1
} else {
//Above logic same when contact number is blank
//Not considerd Patient ID #
var ppID = "NCPID".concat(pSheet.getLastRow());
//Untested
pSheet.getRange(pLastRow, 1).clear();
pSheet.getRange(pLastRow, 1).setValue(ppID.toString());
//till above
for (var colN = 2; colN <= 9; colN++) {
var actCellVal = inputSheet.getRange(i, colN).getValue();
if (colN == 6 || colN == 8) {
pSheet.getRange(pLastRow, colN).setValue(actCellVal)
} else {
pSheet.getRange(pLastRow, colN).setValue(actCellVal.toString().toUpperCase())
pSheet.getRange(pLastRow, 9).setValue(todayDate);
}
}
pLastRow = pLastRow + 1
}
}
//this.ClearAnySheet("InputFromHospital")
};
Sample file available here.

I believe your goal as follows.
You want to reduce the process cost of your script.
Modified script:
function feedToMasterAndPolice_b() {
var ssbook = SpreadsheetApp.getActiveSpreadsheet();
var srcSheet = ssbook.getSheetByName('MasterPatientData');
var sLastRow = srcSheet.getLastRow() + 1;
var inputSheet = ssbook.getSheetByName('InputFromHospital');
var pSheet = ssbook.getSheetByName('DataToPolice');
var pLastRow = pSheet.getLastRow() + 1;
var todayDate = Utilities.formatDate(new Date(), "IST", "dd/MMM/yyyy");
var [, ...values] = inputSheet.getDataRange().getValues();
var { masterPatientData, dataToPolice } = values.reduce((o, [c1, c2, c3, c4, c5, c6, c7, c8], i) => {
if (c5.toString() != "") {
var last = sLastRow - 1 + o.masterPatientData.length;
o.masterPatientData.push([
`PID${last}`,
...[c2, c3, c4, c5, c6, c7, c8].map((c, j) => [3, 4, 6].includes(j) ? c : c.toString().toUpperCase()),
`=if(A${last + 1}<>"",TODAY()-F${last + 1},"")`,
`=if(I${last + 1}<='Admin'!\$C\$2,"YES","NO")`,
todayDate,
]);
} else {
var last = pLastRow - 1 + o.dataToPolice.length;
o.dataToPolice.push([
`NCPID${last}`,
...[c2, c3, c4, c5, c6, c7, c8].map((c, j) => [4, 6].includes(j) ? c : c.toString().toUpperCase()),
todayDate,
]);
}
return o;
}, { masterPatientData: [], dataToPolice: [] });
if (masterPatientData.length > 0) {
srcSheet.getRange(sLastRow, 1, masterPatientData.length, masterPatientData[0].length).setValues(masterPatientData);
}
if (dataToPolice.length > 0) {
pSheet.getRange(pLastRow, 1, dataToPolice.length, dataToPolice[0].length).setValues(dataToPolice);
}
}
Note:
Unfortunately, I cannot test above modified script with your actual situation. So when my proposed script cannot be used for your actual situation, can you provide the sample Spreadsheet for replicating the issue? By this, I would like to confirm it.
References:
Benchmark: Reading and Writing Spreadsheet using Google Apps Script
getValues()
setValues(values)

Related

How to sorting within same date data based on time in google sheet using Apps Script?

I want to sort data based on date(Column A) and start time(Column I). If date column have more than 1 same date then again shorting by start time within this same date contain rows.
i have a code already that sorting by date.
function autoSort() {
var headerRows = 1;
var sortFirst = 1; // 1 is Column "A"
var sortFirstAsc = false; // When it's "true", the order is ascending.
var sortSecond = 9; // 3 is Column "C"
var sortSecondAsc = false; // When it's "true", the order is ['OPEN','YES','NO'].
// Retrieve values from Spreadsheet.
var activeSheet = SpreadsheetApp.getActiveSheet();
var sheetName = activeSheet.getSheetName(); //name of sheet to be sorted
var sheet = SpreadsheetApp.getActive().getSheetByName(sheetName);
var range = sheet.getRange(headerRows+1, 1, sheet.getLastRow()-headerRows, sheet.getLastColumn());
var values = range.getValues();
// Sort the date of column "A".
var s1 = sortFirstAsc ? 1 : -1;
values.sort(function(a, b) {return (a[sortFirst - 1] < b[sortFirst - 1] ? -s1 : s1)});
// Sort the values of column "C" with the custom sort using the keys.
var sortOrder = ['OPEN','YES','NO'];
var s2 = sortSecondAsc ? 1 : -1;
values.sort(function(a, b) {
var i1 = sortOrder.indexOf(a[sortSecond - 1]);
var i2 = sortOrder.indexOf(b[sortSecond - 1]);
var vlen = values.length;
return s2 * ((i1 > -1 ? i1 : vlen) - (i2 > -1 ? i2 : vlen));
});
sheet.getRange(2, 1, values.length, values[0].length).setValues(values);
}

Insert timestamp when value paste/edits in whole row in google sheets, only insert to blank cell

I'm trying to insert a timestamp into a cell in the "timestamp" column with the same row index as the edited or pasted value in the "status" column, only insert a timestamp in to an empty cell in "timestamp" column, while skipping the cell that already has a value in the "timestamp" column.
I'd also like to convert the date to a number, such as yymmddHHmmss*1000 + seri number column "No." Exp: If the timestamp is 22:01:20 14:08:05 and the sequence number in column "No." is 678, then value I want to insert into column "ID" is 2201201408050678.
I need to use the column header as a reference in this code to ensure that the function works correctly when the column index changes.
Issue: When changing many rows, this code simply repeats the original function for each selected cell. It gets the job done, but not too quickly.
How can I improve code speed when a multi-row update occurs?
No.
Timestamp
ID
Status
20
24:01:22 15:01:30
2201241501300020
Approved
17
Process
16
24:01:22 15:59:10
2201241559100016
Approved
16
function neworder2_onEdit(e) {
var sheet = e.range.getSheet();
if ((sheet.getSheetName() == 'RETAIL_ORDER') || (sheet.getSheetName() == 'HAMPER_ORDER') || (sheet.getSheetName() == 'SEA FOOD_ORDER') || (sheet.getSheetName() == 'GARDEN_ORDER'))
{
var col = e.range.columnStart;
var col_header = sheet.getRange(1,col).getValue();
if (col_header != 'Status') return;
var headers = sheet.getRange(1,1,1,sheet.getLastColumn()).getValues()[0];
var timestamp_col = headers.indexOf('Timestamp') + 1;
var num_col = headers.indexOf('No.') + 1;
var id_col = headers.indexOf('ID') + 1;
var row_start = e.range.rowStart;
var row_end = e.range.rowEnd;
if (sheet.getRange(row_start,col).getValue() != 'Approved') return;
var tz = SpreadsheetApp.getActiveSpreadsheet().getSpreadsheetTimeZone();
var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');
for (let row = row_start; row <= row_end; row++) {
var timestamp_cell = sheet.getRange(row, timestamp_col);
if (timestamp_cell.getValue() !== '') continue;
timestamp_cell.setValue(timestamp).setNumberFormat('yy:MM:dd HH:mm:ss');
var num = sheet.getRange(row,num_col).getValue().toString().padStart(4,'0');
var id = timestamp.replace(/\D/g,'') + num;
var id_cell = sheet.getRange(row,id_col);
id_cell.setValue(id);
}
}
}
I believe your goal is as follows.
Your script works fine. You want to reduce the process cost of the script.
In this case, how about the following modification?
Modified script:
function neworder2_onEdit(e) {
var sheet = e.range.getSheet();
if (['RETAIL_ORDER', 'HAMPER_ORDER', 'SEA FOOD_ORDER', 'GARDEN_ORDER'].includes(sheet.getSheetName())) { // Modified
var col = e.range.columnStart;
var col_header = sheet.getRange(1, col).getValue();
if (col_header != 'Status') return;
var row_start = e.range.rowStart;
var row_end = e.range.rowEnd;
// I modified below script.
var values = sheet.getRange(row_start, 1, row_end - row_start + 1, 4).getValues();
if (!values.some(r => r[3] == 'Approved')) return;
var tz = e.source.getSpreadsheetTimeZone();
var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');
var res = values.map(([a, b, c]) => (a == "" || b != "") ? [b, c] : [timestamp, timestamp.replace(/\D/g, '') + a.toString().padStart(4, '0')]);
sheet.getRange(row_start, 2, row_end - row_start + 1, 2).setValues(res);
sheet.getRange(row_start, 2, row_end - row_start + 1, 1).setNumberFormat('yy:MM:dd HH:mm:ss');
}
}
In this modification, after the array was created using the script in your for loop, the array was put to the sheet.
Reference:
map()
Added 1:
From your following replying,
When "status", "ID" or "timstamp" column index change, in case I want to insert column then our code not working. Can we use column header (status, timestamp, ID, No.) as pramameter for my cript? Can you give suggestion to do this?
In this case, how about the following sample script?
Sample script:
function onEdit(e) {
var sheet = e.range.getSheet();
if (['RETAIL_ORDER', 'HAMPER_ORDER', 'SEA FOOD_ORDER', 'GARDEN_ORDER'].includes(sheet.getSheetName())) { // Modified
var col = e.range.columnStart;
var header = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0].map(h => h.toLowerCase()); // Added
var obj = header.reduce((o, e, i) => (o[e] = i, o), {});
var col_header = header[col - 1]; // Modified
if (col_header != 'status') return;
var row_start = e.range.rowStart;
var row_end = e.range.rowEnd;
// I modified below script.
var values = sheet.getRange(row_start, 1, row_end - row_start + 1, header.length).getValues();
if (!values.some(r => r[obj["status"]] == 'Approved')) return;
var tz = e.source.getSpreadsheetTimeZone();
var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');
var res = values.map(r => {
if (!(r[obj["no."]] == "" || r[obj["timestamp"]] != "")) {
r[obj["timestamp"]] = timestamp;
r[obj["id"]] = timestamp.replace(/\D/g, '') + r[obj["no."]].toString().padStart(4, '0');
}
return r;
});
sheet.getRange(row_start, 1, row_end - row_start + 1, res[0].length).setValues(res);
sheet.getRange(row_start, [obj["timestamp"]] + 1, row_end - row_start + 1).setNumberFormat('yy:MM:dd HH:mm:ss');
}
}
In this script, from your question, it supposes that the header values are No.,Timestamp,ID,Status. Please be careful this.
Added 2:
From your following new issue,
It's working but there is some issue with my sheet. when scipt ran that paste value to all column, some of theme using arrayformula so all column use arrayformula will get error "#REF!" Can we just paste value to column timestame, id.
In this case, how about the following sample script?
Sample script:
function onEdit(e) {
var sheet = e.range.getSheet();
if (['RETAIL_ORDER', 'HAMPER_ORDER', 'SEA FOOD_ORDER', 'GARDEN_ORDER'].includes(sheet.getSheetName())) { // Modified
var col = e.range.columnStart;
var header = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues()[0].map(h => h.toLowerCase()); // Added
var obj = header.reduce((o, e, i) => (o[e] = i, o), {});
var col_header = header[col - 1]; // Modified
if (col_header != 'status') return;
var row_start = e.range.rowStart;
var row_end = e.range.rowEnd;
// I modified below script.
var values = sheet.getRange(row_start, 1, row_end - row_start + 1, header.length).getValues();
if (!values.some(r => r[obj["status"]] == 'Approved')) return;
var tz = e.source.getSpreadsheetTimeZone();
var timestamp = Utilities.formatDate(new Date(), tz, 'yy-MM-dd HH:mm:ss');
var res = values.map(r => {
if (r[obj["no."]] != "" && r[obj["timestamp"]] == "" && r[obj["status"]] == "Approved") {
r[obj["timestamp"]] = timestamp;
r[obj["id"]] = timestamp.replace(/\D/g, '') + r[obj["no."]].toString().padStart(4, '0');
}
r.shift();
return r;
});
sheet.getRange(row_start, 2, row_end - row_start + 1, res[0].length).setValues(res);
sheet.getRange(row_start, [obj["timestamp"]] + 2, row_end - row_start + 1).setNumberFormat('yy:MM:dd HH:mm:ss');
}
}

How to Include more sheets to write incremental number

I am using this function which works upon submitting the entry via Google Forms. Currently the below script is adding increment number to the Sheet1 Last empty row
I want to include two more sheets where same incremental number will be added to last empty row.
Column will be same where increment number is pasting that is Column A but Sheet1 data starts from Row2 and Sheet2 and Sheet3 Data starts from row6.
Your help will be much appreciated.
function uPDATEiT() {
var aiColumnName = 'A'; //Sheet1,Sheet2,Sheet3 same column
var requieredColName = 'C' //it is just for Sheet1
var ss = SpreadsheetApp.getActiveSpreadsheet()
var worksheet = ss.getSheetByName('Sheet1','Sheet2','Sheet3')
var aiColRange = worksheet.getRange(aiColumnName + '1:' + aiColumnName + '1000');
var aiCol = aiColRange.getValues();
var aiColIndex = aiColRange.getColumn();
var reqCol = worksheet.getRange(requieredColName + '1:' + requieredColName + '1000').getValues();
var maxSeq = 0;
for (var i = 0; i <= aiCol.length; i++) {
if (parseInt(aiCol[i], 10) > maxSeq) { maxSeq = aiCol[i]; }
}
for (var i = 0; i <= aiCol.length; i++) {
if (('' + reqCol[i]).length > 0 && ('' + aiCol[i]).length === 0) {
maxSeq++;
worksheet.getRange(i + 1, aiColIndex).setValue(maxSeq);
}
}
}
You can use this formula in Sheet2!A4 and Sheet3!A4:
={"ID's";ARRAYFORMULA(IF(B5:B<>"",vlookup(B5:B,{'Data Sheet (Sheet1)'!$B$2:$B,'Data Sheet (Sheet1)'!$A$2:$A},2,false),""))}
What it does?
Check if column B is not empty, then get the id from Sheet1 based on the matched name(column B) as a key in the vlookup()
Output:
Original Sheet2:
Sheet 2 using the formula:
Update
If you just want to append your maxSeq in Sheet2, you can use this:
Code:
function uPDATEiT() {
var aiColumnName = 'A'; //Sheet1,Sheet2,Sheet3 same column
var requieredColName = 'C' //it is just for Sheet1
var ss = SpreadsheetApp.getActiveSpreadsheet()
var worksheet = ss.getSheetByName('Data Sheet (Sheet1)')
var sheet2 = ss.getSheetByName('Sheet2')
Logger.log(worksheet)
Logger.log(sheet2.getLastRow())
var aiColRange = worksheet.getRange(aiColumnName + '1:' + aiColumnName + '1000');
var aiCol = aiColRange.getValues();
var aiColIndex = aiColRange.getColumn();
var reqCol = worksheet.getRange(requieredColName + '1:' + requieredColName + '1000').getValues();
var maxSeq = 0;
for (var i = 0; i <= aiCol.length; i++) {
if (parseInt(aiCol[i], 10) > maxSeq) { maxSeq = aiCol[i]; }
}
for (var i = 0; i <= aiCol.length; i++) {
if (('' + reqCol[i]).length > 0 && ('' + aiCol[i]).length === 0) {
maxSeq++;
worksheet.getRange(i + 1, aiColIndex).setValue(maxSeq);
sheet2.getRange(sheet2.getLastRow()+1,aiColIndex).setValue(maxSeq);
}
}
}
Get the last row in the sheet that contains data using getLastRow() , then increment it by 1.
Output:

Code to delete outdated entries on a google sheet

I found this code on here which should work perfectly for me. Was just hoping someone could change the code to delete entries that have dates that are 2 weeks old or older. So if the script were to run today, it would delete any rows that are October 26th or older.
function DeleteOldEntries() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("MASTER");
var datarange = sheet.getDataRange();
var lastrow = datarange.getLastRow();
var values = datarange.getValues();// get all data in a 2D array
var currentDate = new Date();//today
for (i=lastrow;i>=3;i--) {
var tempDate = values[i-1][2];// arrays are 0 indexed so row1 = values[0] and col3 = [2]
if ((tempDate!=NaN) && (tempDate <= currentDate))
{
sheet.deleteRow(i);
}//closes if
}//closes for loop
}//closes function
Deleting Rows in a forEach loop
function DeleteOldEntries() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName("MASTER");
const sr = 3;//guessing data start on row 3
const vs = sh.getRange(sr, 1, sh.getLastRow() - sr + 1, sh.getLastColumn()).getValues();
let d = 0;//delete counter
const dtv = new Date(new Date().getFullYear(), new Date().getMonth(), new Date().getDate() - 15).valueOf();
vs.forEach((r, i) => {
let cdt = new Date(r[2]);//assume date is in column 3
let cdtv = new Date(cdt.getFullYear(), cdt.getMonth(), cdt.getDate()).valueOf();
if (cdtv < dtv) {
sh.deleRow(i + sr - d++);
}
});
}
Date.valueOf()
I believe your goal is as follows.
From your script and question, you want to delete the rows when the date of column "C" is before 2 weeks from today.
In this case, how about the following modification? In your script, when the value of column "C" is the date object, you are comparing the date object.
From:
var currentDate = new Date();//today
for (i=lastrow;i>=3;i--) {
var tempDate = values[i-1][2];// arrays are 0 indexed so row1 = values[0] and col3 = [2]
if ((tempDate!=NaN) && (tempDate <= currentDate))
{
sheet.deleteRow(i);
}//closes if
}//closes for loop
}//closes function
To:
var currentDate = new Date();
currentDate.setDate(currentDate.getDate() - 14); // Added: This means before 2 weeks from today.
var d = currentDate.getTime(); // Added
for (i = lastrow; i >= 3; i--) {
var tempDate = values[i - 1][2];
if ((tempDate != NaN) && (tempDate.getTime() <= d)) { // Modified
sheet.deleteRow(i);
}
}
}
References:
getDate()
setDate()
Compare two dates with JavaScript

How to automatically add a timestamp in google spreadsheet

I have a sheet in my Google spreadsheet that contains 5 cells, the first 3 contains only words while the last 2 contains time, specifically a timestamp.
cell 1 = data
cell 2 = data
cell 3 = data
cell 4 = time start
cell 5 = time ended
Now, what I want is when cell 1 is supplied with data, a timestamp will automatically appear in cell 4. And when cell 2 and cell 3 is supplied with data, a timestamp will be the new value for cell 5.
My friend give me a code, that should pasted in Script editor:
function readRows() {
var sheet = SpreadsheetApp.getActiveSheet();
var rows = sheet.getDataRange();
var numRows = rows.getNumRows();
var values = rows.getValues();
for (var i = 0; i <= numRows - 1; i++) {
var row = values[i];
Logger.log(row);
}
};
And
function onOpen() {
var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
var entries = [{
name : "Read Data",
functionName : "readRows"
}];
spreadsheet.addMenu("Script Center Menu", entries);
};
function timestamp() {
return new Date()
}
and this code is pasted in =IF(B6="","",timestamp(B6))cell 4 and this one =IF(D6="","",timestamp(C6&B6)) is on cell 5. in his example tracker its working. But when i copied it to mine, the output in cell 4 and cell 5 is the Date today and not the time.
can anyone help me? why does it output the date and not the time?
You can refer this tutorial, if this helps.
In the script code, change
var timestamp_format = "MM-dd-yyyy"; // Timestamp Format.
to
var timestamp_format = "MM-dd-yyyy hh:mm:ss"; // Timestamp Format.
This should probably help you.
I just came across this problem and I modified the code provided by Internet Geeks.
Their code works by updating a specified column, the timestamp is inserted in the same row in another specified column.
What I changed is that I separated the date and the time, because the timestamp is a string, not a date format. My way is useful for generating graphs.
It works by specifying the column to track for changes, and then creating an upDate and upTime columns for the date and time respectively.
function onEdit(event) {
var timezone = "GMT+1";
var date_format = "MM/dd/yyyy";
var time_format = "hh:mm";
var updateColName = "Резултат";
var DateColName = "upDate";
var TimeColName = "upTime";
var sheet = event.source.getActiveSheet(); // All sheets
// var sheet = event.source.getSheetByName('Test'); //Name of the sheet where you want to run this script.
var actRng = event.source.getActiveRange();
var editColumn = actRng.getColumn();
var index = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(DateColName);
var timeCol = headers[0].indexOf(TimeColName);
var updateCol = headers[0].indexOf(updateColName);
updateCol = updateCol + 1;
if (dateCol > -1 && timeCol > -1 && index > 1 && editColumn == updateCol) {
// only timestamp if 'Last Updated' header exists, but not in the header row itself!
var cellDate = sheet.getRange(index, dateCol + 1);
var cellTime = sheet.getRange(index, timeCol + 1);
var date = Utilities.formatDate(new Date(), timezone, date_format);
var time = Utilities.formatDate(new Date(), timezone, time_format);
cellDate.setValue(date);
cellTime.setValue(time);
}
}
Hope this helps people.
Updated and simpler code
function onEdit(e) {
var sh = e.source.getActiveSheet();
var sheets = ['Sheet1']; // Which sheets to run the code.
// Columns with the data to be tracked. 1 = A, 2 = B...
var ind = [1, 2, 3].indexOf(e.range.columnStart);
// Which columns to have the timestamp, related to the data cells.
// Data in 1 (A) will have the timestamp in 4 (D)
var stampCols = [4, 5, 6]
if(sheets.indexOf(sh.getName()) == -1 || ind == -1) return;
// Insert/Update the timestamp.
var timestampCell = sh.getRange(e.range.rowStart, stampCols[ind]);
timestampCell.setValue(typeof e.value == 'object' ? null : new Date());
}
I made a slightly different version, based also on the code from Internet Geeks
In order to support multiple named sheets, and because Google Sheets Script doesn't currently support Array.prototype.includes(), I included the polyfill mentioned here
Also, in my version, the timestamp marks the date of creation of that row's cell, not the date of the last update as in the other scripts provided here.
function onEdit(event) {
var sheetNames = [
'Pounds £',
'Euros €'
]
var sheet = event.source.getActiveSheet();
if (sheetNames.includes(sheet.getName())){
var timezone = "GMT";
var dateFormat = "MM/dd/yyyy";
var updateColName = "Paid for ...";
var dateColName = "Date";
var actRng = sheet.getActiveRange();
var editColumn = actRng.getColumn();
var rowIndex = actRng.getRowIndex();
var headers = sheet.getRange(1, 1, 1, sheet.getLastColumn()).getValues();
var dateCol = headers[0].indexOf(dateColName) + 1;
var updateCol = headers[0].indexOf(updateColName) + 1;
var dateCell = sheet.getRange(rowIndex, dateCol);
if (dateCol > 0 && rowIndex > 1 && editColumn == updateCol && dateCell.isBlank())
{
dateCell.setValue(Utilities.formatDate(new Date(), timezone, dateFormat));
}
}
}
// https://stackoverflow.com/a/51774307/349169
// https://tc39.github.io/ecma262/#sec-array.prototype.includes
if (!Array.prototype.includes) {
Object.defineProperty(Array.prototype, 'includes', {
value: function(searchElement, fromIndex) {
if (this == null) {
throw new TypeError('"this" is null or not defined');
}
// 1. Let O be ? ToObject(this value).
var o = Object(this);
// 2. Let len be ? ToLength(? Get(O, "length")).
var len = o.length >>> 0;
// 3. If len is 0, return false.
if (len === 0) {
return false;
}
// 4. Let n be ? ToInteger(fromIndex).
// (If fromIndex is undefined, this step produces the value 0.)
var n = fromIndex | 0;
// 5. If n ≥ 0, then
// a. Let k be n.
// 6. Else n < 0,
// a. Let k be len + n.
// b. If k < 0, let k be 0.
var k = Math.max(n >= 0 ? n : len - Math.abs(n), 0);
function sameValueZero(x, y) {
return x === y || (typeof x === 'number' && typeof y === 'number' && isNaN(x) && isNaN(y));
}
// 7. Repeat, while k < len
while (k < len) {
// a. Let elementK be the result of ? Get(O, ! ToString(k)).
// b. If SameValueZero(searchElement, elementK) is true, return true.
if (sameValueZero(o[k], searchElement)) {
return true;
}
// c. Increase k by 1.
k++;
}
// 8. Return false
return false;
}
});
}