Google Apps Script for moving and timestamping row in Google Sheets - javascript

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()

Related

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
}
}

Looping through data and setting IndexMatch formula based on cell value - too slow because of .getValue()

I have a code which loops through my rows and depending on what the value is in Column 2, returns different types of IndexMatch formulas. My code works very well and puts the formulas in the relevant columns, however it is very slow.
I get a message that .getValue() is very heavy and therefore slows down the code.
Now I need to figure out how to improve the speed of the code but since I am new to coding, I am not particularly sure how to go at this problem.
Here is what I have:
function setFormulas() {
var ss = SpreadsheetApp.getActiveSpreadsheet(); //gets the workbook being used
var sheet = ss.getSheetByName("Open Requests"); //gets the active worksheet
var sheetnametoWatch ="Open Requests"; //defines sheet
var columnnumbertoWatch = 14
var valuetoWatch = ""
var CostCenter = "'Open Requests'!"
var lastrow = sheet.getLastRow();
var datarange = sheet.getRange(11,2,lastrow -1,50).getValues()
var row = 10
for (i=0;i<datarange.length;i++){
var rowID = 10 + i + 1
if (datarange[i][0] == "CC"){
var addedCell = '=CONCATENATE(F'+rowID+',N'+rowID+',K'+rowID+')';
sheet.getRange(row + i+1,35).setFormula(addedCell);
var MatchedCell = '=INDEX(Input!$Q:$Q,MATCH('+CostCenter+'$AI$'+rowID+',Input!$K:$K,0))';
sheet.getRange(row + i+1,17).setFormula(MatchedCell);
sheet.getRange(row + i+1,18).setValue("kristin.j#ni.com")
}
else if (datarange[i][0] == "CC + TM1"){
var addedCell = '=CONCATENATE(F'+rowID+',N'+rowID+',K'+rowID+')';
sheet.getRange(row + i+1,35).setFormula(addedCell);
var MatchedCell = '=INDEX(Input!$Q:$Q,MATCH('+CostCenter+'$AI$'+rowID+',Input!$K:$K,0))';
sheet.getRange(row + i+1,17).setFormula(MatchedCell);
sheet.getRange(row + i+1,18).setValue("kristin.j#ni.com")
}
else if (datarange[i][0] == "GC"){
sheet.getRange(row + i+1,18).setValue("kristin.j#ni.com")
}
}
}
So as you can see, I am looping through the data and then depending on whether the column = CC, CC+TM1, GC it will set other cells equal to the relevant formula.
It would be great if somebody can help me in speeding up this process.
I'm having some trouble following exactly what the code does, but at a high level, I think what you want to do is this: instead of setting formulas for particular cells as you cycle through your for loop, use the loop to figure out which cells should be set to which formulas and store that information. Then, at the end of the for loop, call sheet.getRange(x,y).setFormula(Formula); just a few times, perhaps once for each formula.
Does that seem like it could work?

Hide all but today's columns on four sheets (Google Spreadsheets script)

Goal: I'm trying to create a behavior tracker for four classes in Google Spreadsheets. The tracker has nine sheets: Class7A, Class7B, Class8A, Class8B, and Mon-Fri summary sheets. The goal was for each ClassXX sheet to have behavior tracking information for an entire week, but for the default view to show only the current day's information.
Attempts: During initial workup (with only the Class7A sheet created), I got this to work using a modification of the script found here (Thank you Jacob Jan Tuinstra!): Optimize Google Script for Hiding Columns
I modified it to check the value in the third row of each column (which held a 1 for Monday, 2 for Tuesday, etc), and if it did not match the numerical equivalent for the day of the week (var d = new Date(); var n = d.getDay();), then it would hide that column. This process was somewhat slow - I'm assuming because of the iterating through each column - but it worked.
Quite excited, I went ahead and added the rest of the sheets, and tried again - but the code as written, seems to affect only the current sheet. I tried modifying it by replacing var sheet = ss.getSheets()[0]; with for script that iterated through the columns, until i>4 (I've since lost that piece of code), with no luck.
Deciding to go back and try adapting the original version of the script to instead explicitly run multiple times for each named sheet, I found the that script no longer seems to work at all. I get various version of "cannot find XX function in sheet" or "cannot find XX function in Range."
Source: A shared version (with student info scrubbed) can be found here: https://docs.google.com/spreadsheets/d/1OMq4a4_Gh_xyNk_IRy-mwJn5Hq36RXmdAzTzx7dGii0/edit?usp=sharing (editing is on).
Stretch Goal: Ultimately, I need to get this to reliably show only the current day's columns (either through preset ranges (same for each sheet), or the 1-5 values), and I need it to do so for all four ClassXX sheets, but not the summary pages (and preferably more quickly than the iterations). If necessary, I can remove the summary pages and set them up externally, but that's not my first preference. I would deeply appreciate any help with this; so far my attempts have seemed to only take me backwards.
Thanks!
Current code:
function onOpen() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// create menu
var menu = [
{name: "Show Today Only", functionName: "hideColumn"},
{name: "Show All Days", functionName: "showColumn"},
{name: "Clear Week - WARNING will delete all data", functionName: "clearWeek"}
];
// add to menu
ss.addMenu("Show Days", menu);
}
var d = new Date();
var n = d.getDay();
function hideColumn() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheets()[0];
// get data
var data = sheet.getDataRange();
// get number of columns
var lastCol = data.getLastColumn()+1;
Logger.log(lastCol);
// itterate through columns
for(var i=1; i<lastCol; i++) {
if(data.getCell(2, i).getValue() != n) {
sheet.hideColumns(i);
}
}
}
function showColumn() {
// get active spreadsheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
// get first sheet
var sheet = ss.getSheets()[0];
// get data
var data = sheet.getDataRange();
// get number of columns
var lastCol = data.getLastColumn();
// show all columns
sheet.showColumns(1, lastCol);
}
I cannot recreate the problem of the script not working at all, it's working fine for Class7A so that part is working fine.
So let's look at the two other problems:
Applying this to all Sheets
Speeding up the script
First let's create some globals we use in both functions
var d = new Date();
var n = d.getDay();
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheetNames = ss.getSheets().map(function(sheet) {return sheet.getName();});
var classSheets = sheetNames.filter(function(sheetName) {return sheetName.match("Class")});
Now we can iterate over classSheets and get the sheet by name and hide columns in each.
However hiding each individual column is very slow.
The sheet is built very structured, every week has 12 columns (except for friday which doesn't have the grey bar), so we can just calculate the ranges we want to hide.
function hideColumn() {
classSheets.map(function(sheetName){
var sheet = ss.getSheetByName(sheetName);
if (n == 1) {
// Hide everything after the first three columns + Monday
sheet.hideColumns(3 + 11, 12 * 4);
} else if (n == 5) {
// Hide everything to the left except the leftmost three columns
sheet.hideColumns(3, 4 * 12);
} else {
// Hide everything left of the current day
sheet.hideColumns(3, (n - 1) * 12);
// Hide everything after the current day
sheet.hideColumns(3 + n * 12, (5 - n) * 12 - 1);
}
});
}
Lastly we can shorten showColumn
function showColumn() {
classSheets.map(function(sheetName){
var sheet = ss.getSheetByName(sheetName);
var lastCol = sheet.getLastColumn();
sheet.showColumns(1, lastCol);
});
}

Google Sheets Wildcards in if statements

I have a Google Sheet that has form responses. The e-mail address was not required, however it should have been. Either way, I am trying to back-fill the e-mail addresses (in order to make them ready to import as Spiceworks tickets, but I digress). I am going through and typing in usernames, but I want Sheets to auto-fill the domain. I was thinking I could do this by having it detect that the string ended in #, and then just adding the domain to it. Currently I have:
// assumes source data in sheet named Done 14-15
// test column with done is col 9 or I
if(s.getName() == "Done 14-15" && r.getColumn() == 9 && r.getValue() == "?#" ) {
var row = r.getRow();
var value = r.getValue();
r.setValue(value + "example.org");
var numColumns = s.getLastColumn();
s.getRange(row, 1, 1, numColumns).copyTo(target);
}
As you can see, I have a question mark for a wildcard. I have tried using an asterisk or a percentage sign as well, and not gotten anywhere. It will replace if I have literally ?# in there, but I want it to take anything# and append our domain.
RegEx should solve your problem.
Replace the r.getValue() == "?#" with
var regEx = new RegExp('.*#$')
if (regEx.test(r.getValue())) {
// your code
}
Instead of r.getValue() == "?#" you can write r.getValue().endsWith("#")
The email addresses can be easily updated like this:
var newValue = event.value.replace(/#$/,'#example.org');
Where the match is not found, the replacement will not happen... and newValue will equal the original value. Instead of checking for the match before deciding to do something, I'm suggesting doing it then checking the result.
Since you are entering the email addresses by hand, this is a good application of the onEdit() simple trigger and its event object.
function onEdit(event) {
var r = event.range;
var s = r.getSheet();
if (s.getName() == "Done 14-15" && r.getColumn() == 9 && r.getRow() > 1) {
// Replace an # at the end of the string with domain
var newValue = event.value.replace(/#$/,'#example.org');
// If value changed, write it back to spreadsheet
if (event.value !== newValue) {
event.range.setValue(newValue);
}
}
}
If you have rows that have already been edited and need to be checked, this function will take care of them. It uses the technique from How can I test a trigger function in GAS? to create a fake event, then passes it to the onEdit() trigger function.
// Call onEdit for each row in conversion sheet
function convertAllEmails() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName( "Done 14-15" );
var lastRow = sheet.getLastRow();
for (var row=1; row<lastRow; row++) {
var fakeEvent = {
range: sheet.getRange(row,9);
value: range.getValue();
};
onEdit( fakeEvent );
}
}

Adding name to last edited (google script)

Currently i have the following google script:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
var r = s.getActiveCell();
if(s.getName()=='Setup BRF'){
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow();
var time = new Date();
time = Utilities.formatDate(time, "GMT", "dd-MM-yy' , 'HH:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('J1').setValue(time);
};
};
};
Everytime i edit something on the sheet called Setup BRF it updates the time and date in J1 to show when it was last updated/edited.
My question is if its possible to add a name to lets cell K1 which shows who last updated this sheet. My skill with javascript is nihil so any kind of help is appreciated.
You could use the event object passed to the function
So change the definition to
function onEdit(e) {
and then you can use e.user to get to the user
Something like this
if( r.getColumn() != 2 ) { //checks the column
var row = r.getRow(),
time = new Date(),
user = e.user;
time = Utilities.formatDate(time, "GMT", "dd-MM-yy' , 'HH:mm:ss");
SpreadsheetApp.getActiveSheet().getRange('J1').setValue(time);
SpreadsheetApp.getActiveSheet().getRange('K1').setValue(user);
};

Categories