Running an onEdit Script on selected sheets only - javascript

I am new to coding Scripts but have managed to copy and edit (bodge...) a script that will onEdit, put a custom timestamp into the cell next to the cell where the edit took place. The script targets x2 onEdit columns (3,6), timestamping the cell offset(-1).
The workbook has developed to include multiple sheets now, most of which this script is not appropriate to run on, but I cannot figure out how to specify which sheets it should run on.
The below is the script I have cobbled together which works across all sheets, which I would now like to restrict to specific sheets only.
function onEdit(e) {
var colsToWatch = [3,6],
offset = [-1,-1],
ind = colsToWatch.indexOf(e.range.columnStart);
if (ind === -1 || e.range.rowStart === 1) return;
e.range.offset(0, offset[ind])
.setValue(!e.value ? null : Utilities.formatDate(new Date(), "GMT+1", "dd MMM yyyy HH:mm.ss"))
}
Please can someone help me by providing the code to run the scripts on specific sheets only. I would also be grateful for any suggestions of a simpler script to achieve the same aim, especially if it eliminates the timestamp being overwritten if the onEdit cell is subsequently edited after the initial edit.
Many thanks!!

function onEdit(e) {
const sh = e.range.getSheet();
const shts = ['Sheet1','Sheet2'];
const idx = shts.indexOf(sh.getName());
if(~idx ) {
//Whatever code you put here will only run on Sheet1 or Sheet2
}
}
This works for me (on on Sheet1 and Sheet2):
function onEdit(e) {
//e.source.toast('entry');
const sh = e.range.getSheet();
const shts = ['Sheet1','Sheet2'];
const idx = shts.indexOf(sh.getName());
if(~idx ) {
let rg = sh.getRange(e.range.rowStart,9);
if(rg.isBlank()) {
rg.setValue(new Date());
}
}
}
JavaScript Reference

Related

Google Apps Script for moving and timestamping row in Google Sheets

Let me start by saying writing scripts is not in my wheelhouse. I hobbled together my first one by by utilizing forums like this, but never really understood how it worked. Now, that script is no longer functioning and I'm stumped as to why. In addition, I'd like to add to that script, but am unsure as to how. Here's my situation and objective...
I have a spread sheet comprised of 2 sheets, and there are 2 things I'd like to happen when a specific dropdown is selected for any row.
First is for that row to move from sheet 1 to sheet 2. I achieved this by using the following script:
function onEdit(event) {
// assumes source data in sheet named Repairs
// target sheet of move to named Closed
// test column with yes/no is col 4 or D
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.source.getActiveRange();
if(s.getName() == "Repairs" && r.getColumn() == 6 && r.getValue() == "Closed") {
var row = r.getRow();
var numColumns = s.getLastColumn();
var targetSheet = ss.getSheetByName("Closed");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
s.deleteRow(row);
}
}
This worked well for sometime, but recently stopped.
In addition to getting this to work again, the second function I would like to incorporate is to add a timestamp.
Ideally, once the row is moved to the 2nd sheet, I would like a column to be added that includes the time/date of the move.
If anyone is able to help with this I'd greatly appreciate it. If more detail is required please let me know and I'll do my best to provide it.
Thanks for reading!
I've modified your script to remove redundant commands. Everything you need to know about the edit is in the event object.
function onEdit(event) {
// assumes source data in sheet named Repairs
// target sheet of move to named Closed
// test column with yes/no is col 4 or D
var s = event.range.getSheet();
if( s.getName() == "Sheet1" && event.range.getColumn() == 6 && event.value == "Closed") {
var row = event.range.getRow();
var numColumns = s.getLastColumn();
var targetSheet = event.source.getSheetByName("Sheet2");
var target = targetSheet.getRange(targetSheet.getLastRow() + 1, 1);
s.getRange(row, 1, 1, numColumns).moveTo(target);
target.offset(0,numColumns).setValue(new Date());
s.deleteRow(row);
}
}
Reference
onEdit(e) event object
Range.offset()

Repeat script until conditions are met

I have a google sheet with a timer set for 5am and everyday I want the sheet to:
Refresh, which includes some links to external data, so I add a 10 second pause. There are approximately 100 rows.
I successfully wrote a formula in column U to tell me if data is imported correctly, and if not it returns the text "ERROR".
Search for "ERROR" in column U.
If there are any "ERRORS" in column U, refresh the page again and wait 10 seconds.
Repeat until there are no "ERRORS"
Only when there are no "ERRORS" in column U, copy the values in column V and paste-values in column W.
I think I am close... I just can't figure out how to tell it to re-run the "IF" portion if still finds "ERRORS". Any input is appreciated!
Here is a link to the file -> https://docs.google.com/spreadsheets/d/1GFN3tXRlqxo9J9iNpZMPk-e-WlNGVjJ4zSqZ1_aEE_U
function HardKeyValues() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Players'), true);
SpreadsheetApp.flush();
Utilities.sleep(10000);
var findVal = spreadsheet.getRange('U:U').getValue()
if(findVal.match('ERROR')){
SpreadsheetApp.flush();
Utilities.sleep(10000)}
else{
spreadsheet.getRange('W:W').activate();
spreadsheet.getRange('V:V').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);}
}
I would not actually use the sleep timer this way
But if I did I would write it like this:
function HardKeyValues() {
const ss = SpreadsheetApp.getActive();
const psh = ss.getSheetByName('Players');
SpreadsheetApp.flush();
Utilities.sleep(10000);
const vs = psh.getRange('U1:U' + psh.getLastRow()).getDisplayValues().flat();
if (vs.find(e => e.includes("ERROR"))) {
SpreadsheetApp.flush();
Utilities.sleep(10000);
} else {
sh.getRange("V1:V" + sh.getLastRow()).copyTo(sh.getRange("W1:W" + sh.getLastRow(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false));
}
}
Using indeterminate ranges like "W:W" often leads to many nulls at the end of the array.
What about using a while loop ?
function HardKeyValues() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.setActiveSheet(spreadsheet.getSheetByName('Players'), true);
SpreadsheetApp.flush();
Utilities.sleep(10000);
var findVal = spreadsheet.getRange('U:U').getValue()
while (findVal.match('ERROR')){
SpreadsheetApp.flush();
Utilities.sleep(10000)}
findVal = spreadsheet.getRange('U:U').getValue()
}
spreadsheet.getRange('W:W').activate();
spreadsheet.getRange('V:V').copyTo(spreadsheet.getActiveRange(),
SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
}
You could maybe try using a for loop, just make sure to have a variable that can eventually meet the requirement for the for loop to stop, otherwise, it will turn into a forever loop ;)

How to sort more then 100 sheets in spreadsheet?

I have google spreadsheet whit more then 100 sheets and i need to sort it by alphabet. Now i have script
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetArray = ss.getSheets();
var sheetNameArray = [];
for(var i = 0; i<sheetArray.length; i++){
sheetNameArray.push(sheetArray[i].getSheetName());
};
sheetNameArray.sort();
sheetNameArray.forEach(function (element) {
var sheet = ss.getSheetByName(element);
ss.setActiveSheet(sheet);
ss.moveActiveSheet(ss.getNumSheets())
})
This run in onEdit. But it's work more then 30 seconds and cannot complete. What should i do?
I think this would work faster because I tested both codes:
function sortSheets() {
const ss = SpreadsheetApp.getActive();
const allSheets = ss.getSheets();
const sheetsAr = allSheets.map(sh=>[sh,sh.getName()]);
sheetsAr.sort((a,b) => a[1].charCodeAt(0)-b[1].charCodeAt(0));
sheetsAr.forEach((v,i)=>{
ss.setActiveSheet(v[0]);
ss.moveActiveSheet(i+1);
});
}
You will gain some performance because I got rid of the for loop.
If this does not work, I am afraid you have to use a time-driven trigger which can run from 6 minutes (if you have a consumer account) or 30 minutes (if you have a business account). You can set up the time driven trigger to run every 1 minute or longer so you can have a sort of "live" adjustments in the orders of the sheet.

Google Sheets Script for loop no longer pasting until after the script has ended or cancelled

For some reason, my code that was working as of yesterday has quit working today. I can't seem to identify what I did to cause the issue and could use some more eyes on it. It was a really simple process but I am extremely new to the language, so I'm sure I'm missing something.
The issue is that the for loop used to update/paste to cell B2 on the sheet, but now it doesn't do that until after I cancel the code or it ends (using whatever the most recent value for num was). It's causing the information to not be updated and so all I get is the information associated with whatever is in cell B2 pasted all the way down through the end of the rows. It used to wait until B2 was updated before copying and pasting to the next row, which took a while but I still had more than enough time before the 5 minute limit since I'm not running a ton of data. My only thought is that I must've made a minor edit that changed this, but I can't track it down in version history. Any help would be greatly appreciated.
function CopyandPasteNewData2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('sheet1');
var lastRow = sh.getLastRow();
var num = 6;
for(var i = 6; i<=lastRow; i++)
{
num;
sh.getRange('A'+num).copyTo(sh.getRange('B2'), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false)
sh.getRange('D2:H2').copyTo(sh.getRange('B'+num+':F'+num), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false)
num++;
}
};
When you iteratively modify the values of a sheet it is always a good practice to use flush() to apply all pending Spreadsheet changes.
Please try the following small modification:
function CopyandPasteNewData2() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sh = ss.getSheetByName('sheet1');
var lastRow = sh.getLastRow();
var num = 6;
for(var i = 6; i<=lastRow; i++)
{
num;
sh.getRange('A'+num).copyTo(sh.getRange('B2'), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false)
sh.getRange('D2:H2').copyTo(sh.getRange('B'+num+':F'+num), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false)
SpreadsheetApp.flush(); // <- new code
num++;
}
};

Edit script to apply across all sheets

This is the script that I am using currently. I want it to apply to all sheets in my document automatically. Please help. The following is the code.
/**
* Creates a Date Stamp if a column is edited.
*/
//CORE VARIABLES
// The column you want to check if something is entered.
var COLUMNTOCHECK = 1;
// Where you want the date time stamp offset from the input location. [row, column]
var DATETIMELOCATION = [0,1];
// Sheet you are working on
var SHEETNAME = '46'
function onEdit(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
//checks that we're on the correct sheet.
if( sheet.getSheetName() == SHEETNAME ) {
var selectedCell = ss.getActiveCell();
//checks the column to ensure it is on the one we want to cause the date to appear.
if( selectedCell.getColumn() == COLUMNTOCHECK) {
var dateTimeCell = selectedCell.offset(DATETIMELOCATION[0],DATETIMELOCATION[1]);
dateTimeCell.setValue(new Date());
}
}
}
I want the script to execute on every sheet so that whenever I add a new sheet the script runs automatically and helps me add a time stamp to column two when I add a value to column 1.
Please help.
I am a complete noob here and as such some detailed explanation will help me immensely.
Basically, just remove the if statement and the code will apply to EVERY sheet.
In the answer given below, I've also used some of the objects returned by the onEdit trigger.
/**
* Creates a Date Stamp if a column is edited.
*/
//CORE VARIABLE
// The column you want to check if something is entered.
var COLUMNTOCHECK = 1;
function onEdit(e) {
var sh = e.range.getSheet();
if (e.range.columnStart == COLUMNTOCHECK){
// edited the right column do something
var targetcell = sh.getRange(e.range.rowStart,2);
targetcell.setValue(new Date());
}
else {
// do nothing
}
}

Categories