I'm new at this so thank you for your help and your patience.
Example: I have a shared spreadsheet with a protected range 'Y:Y' where colleagues update line items. One line item we want to flag when they change it is in column 'X:X'.
My desire is to set up a trigger that runs every 24 hours and it copies the values in column 'X:X' and pastes them into empty or blank values in column 'Y:Y'.
Then a simple DAX formula would compare X to Y. This would simplify a script I'm currently using that runs every time there's an edit.
function OnEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "PTL" ) { //checks that we're on PTL or not
if( s.getActiveCell().getColumn() == 7 ) { //checks that the cell being edited is in column G
var modify = s.getActiveCell().offset(0, 7);
modify.setValue(new Date());
var baseline = s.getActiveCell().offset(0, 6);
if( baseline.getValue() === '' ) //checks if the adjacent cell is empty or not?
baseline.setValue(new Date());
}
}
}
Try it this way:
function OnEdit(e) {
var sh = e.range.getSheet();
if (sh.getName() == "PTL" && e.range.columnStart == 7) {
e.range.offset(0,7).setValue(new Date());
if (e.range.offset(0, 6).getValue() == '')
e.range.offset(0,6).setValue(new Date());
}
}
function notsurewhatyouwant() {
const ss = SpreadsheetApp.getActive();
const sh = ss.getSheetByName('PTL');
const xy = sh.getRange(1, 24, sh.getLastRow(), 2).getValues();
xy.forEach((r, i) => {
if (r[1] == null) {
sh.getRange(i+1,25).setValue(r[0]);
}
});
}
function createTrigger() {
if(ScriptApp.getProjectTriggers().filter(t => t.getHandlerFunction() == "notsurewhatyouwant").length == 0) {
ScriptApp.newTrigger('notsurewhatyouwant').timeBased().everyDays(1).atHour(0).create();
}
}
Related
I am trying to run a Google App script that unhides columns, and then rehides columns based whether the cell reference says Blank or not. This is determined by a drop down in a single cell. I got the script to run fine (if a little clunky) whenever any edit is made, but when I try to add the specification of cell it flags an error with the range.
I'm very new to this so I'm a bit out of my depth so any help is appreciated!
function onEdit(e) {
if (e.range.getA1Notation() === 'B2') {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActive().getSheetByName("Group M0 loot");
sheet.showColumns(9, 35);
var range = sheet.getRange("4:4");
var num_cols = range.getNumColumns();
for (var i = 9; i <= num_cols; i++) {
var value = sheet.getRange(4,i).getValue();
if (value == "Blank") {
sheet.hideColumns(i);
};
}
}
}
Try this:
function onEdit(e) {
const sh = e.range.getSheet();
if (sh.getName() == "Group M0 loot" && e.range.columnStart == 2 && e.range.rowStart == 2) {
sh.showColumns(9, 35);
sh.getRange(4,1,1,sh.getLastColumn()).getValues().flat().forEach((c,i) => {
if(!c) {
sh.hideColumns(i + 1);
}
});
}
}
I have a script that populates with today's date column J when column A is filled.
function Populate() {
var sheetNameToWatch = "MASTER";
var columnNumberToWatch = /* column A */ 1;
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = SpreadsheetApp.getActiveSheet();
var range = sheet.getActiveCell();
var val=sheet.getActiveCell().getValue()
if (sheet.getName() == sheetNameToWatch && range.getColumn() == columnNumberToWatch && val!= "" ) {
var targetCell = sheet.getRange(range.getRow(), range.getColumn()+9
);
targetCell.setValue("" + Utilities.formatDate(new Date(), "GMT", "yyyy-MM-dd"));
}
}
I think it's quite slow and I also would like to fill more columns at once, aside from the date on Column J:
Column I: "No payment"
Column L: "PENDING"
In order to fill multiple columns and try to make it work faster, I've also tested another version:
function Populate2(e)
{
var sheet = e.source.getActiveSheet();
if (sheet.getName() !== 'MASTER'|| e.range.getColumn() !== 1)
{
return;
}
for(var i=0;i<e.range.getNumRows();i++)
{
var offset=e.range.getRow() + i;
sheet.getRange('I'+ offset).setValue("No Payment");
sheet.getRange('J'+ offset).setValue(new Date());
sheet.getRange('L'+ offset).setValue("PENDING");
}
}
The last version has the problem that even if I clean the column A, the values are filled.
Couldn't figure out which version - if any - would be the best approach to improve regarding efficiency, and how.
Can anyone give me a hand?
Thank you in advance.
I believe your goal is as follows.
You want to reduce the process cost of your script.
When the values of column "A" are removed, you want to clear the columns "I", "J", and "L".
In this case, how about the following modification?
Modified script:
function Populate2(e) {
var range = e.range;
var sheet = e.source.getActiveSheet();
if (sheet.getSheetName() !== 'MASTER' || range.columnStart !== 1) {
return;
}
var values = range.getDisplayValues();
var { noPayment, date, pending, clear } = values.reduce((o, [a], i) => {
var row = range.rowStart + i;
if (a == "") {
o.clear.push(...["I", "J", "L"].map(e => e + row));
} else {
o.noPayment.push("I" + row);
o.date.push("J" + row);
o.pending.push("L" + row);
}
return o;
}, { noPayment: [], date: [], pending: [], clear: [] });
if (noPayment.length > 0) {
sheet.getRangeList(noPayment).setValue("No Payment");
sheet.getRangeList(date).setValue(new Date());
sheet.getRangeList(pending).setValue("PENDING");
}
if (clear.length > 0) {
sheet.getRangeList(clear).clearContent();
}
}
In this modification, the values are put to the cells using the range list. And also, the cells are cleared using the range list.
Note:
From your question, this modified script supposes that your function Populate2 is installed as OnEdit trigger. Please be careful about this.
I think that in your script, onEdit simple trigger might be also used. But, I'm not sure about your actual situation. So I used Populate2 in your script.
References:
getRangeList(a1Notations)
Class RangeList
Try this:
function Populate2(e) {
//e.source.toast('Entry');
var sh = e.range.getSheet();
if (sh.getName() !== 'Master' || e.range.columnStart !== 1) { return; }
//e.source.toast('flag1')
let n = e.range.rowEnd - e.range.rowStart + 1;
let dt = new Date();
let i = [...Array.from(new Array(n).keys(),x => ["No Payment"])];
let j = [...Array.from(new Array(n).keys(),x => [dt])];
let l = [...Array.from(new Array(n).keys(),x => ["PENDING"])];
sh.getRange(e.range.rowStart, 9, n).setValues(i);
sh.getRange(e.range.rowStart, 10, n).setValues(j);
sh.getRange(e.range.rowStart, 12, n).setValues(l);
}
or
function Populate2(e) {
//e.source.toast('Entry');
var sh = e.range.getSheet();
if (sh.getName() !== 'Master' || e.range.columnStart !== 1) { return; }
//e.source.toast('flag1')
let n = e.range.rowEnd - e.range.rowStart + 1;
let dt = new Date();
let i = [...Array.from(new Array(n).keys(),x => ["No Payment",dt])];
let l = [...Array.from(new Array(n).keys(),x => ["PENDING"])];
sh.getRange(e.range.rowStart, 9, n, 2).setValues(i);
sh.getRange(e.range.rowStart, 12, n).setValues(l);
}
Array.from()
Array Constructor
I am trying to combine two onEdit functions in a single App Script. I have read through other posts on this subject and feel like I have integrated their suggestions but it is still not working.
Function A (timeStamp) places a time stamp in column B when columnn A is edited
Function B (arhviveRow) copies the edited row to sheet "Inactive" when a check box is marked True on sheet "Active" and then deletes the row from sheet "Active".
Both functions work correctly when run separately, but only the second function works when combined.
function onEdit(e) {
timeStamp();
archiveRow();
//Automatically stamp date and time on line
function timeStamp() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "Active" ) { //checks that we're on Sheet1 or not
var r = s.getActiveCell();
if( r.getColumn() == 1 ) { //checks that the cell being edited is in column A
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === "" ) //checks if the adjacent cell is empty or not?
nextCell.setValue(new Date() + new (Time));
}
}
}
//Archive and delete line
function archiveRow(){
//ColumnNumberToWatch defines which column on sheet to monitor for an edit
var ss = e.source,
sheet = ss.getActiveSheet(),
range = e.range,
targetSheet,
columnNumberToWatch = 9; // column A = 1, B = 2, etc
// "sheet.getName() ===" defines which sheet to watch for the edit
// "targetSheet =" defines sheet to copy data to
if (sheet.getName() === "Active" && e.value === "TRUE" && e.range.columnStart === columnNumberToWatch) {
targetSheet = "Inactive"
} else if (sheet.getName() === "Inactive" && e.value === "FALSE" && e.range.columnStart === columnNumberToWatch) {
targetSheet = "Active"
}
//Copies data to last row in targetSheet and then deletes from source sheet
ss.getSheetByName(targetSheet)
.appendRow(sheet.getRange(e.range.rowStart, 1, 1, sheet.getLastColumn())
.getValues()[0])
sheet.deleteRow(e.range.rowStart);
}
}
Any insights into where I went wrong would be greatly appreciated.
Thanks!
The new Date() object should already contain the time, and there is no Javascript Object for new Date(Time).
It should look like this:
function timeStamp() {
var s = SpreadsheetApp.getActiveSheet();
Logger.log(s);
if( s.getName() == "Active" ) { //checks that we're on Sheet1 or not
var r = s.getActiveCell();
if( r.getColumn() == 1 ) { //checks that the cell being edited is in column A
var nextCell = r.offset(0, 1);
if( nextCell.getValue() === "" ) {//checks if the adjacent cell is empty or not?
nextCell.setValue(new Date().toLocaleString());
}
}
}
}
Sample output for the date:
Active Sheet
Once the checkbox was clicked setting the value to TRUE, appends the row to the "Inactive" sheet.
Inactive Sheet
function onEdit(e) {
const sh = e.range.getSheet();
const name = sh.getName():
if (name == "Active" && e.range.columnStart == 1) {
e.range.offset(0, 1).setValue(Utilities.formatDate(new Date(), Session.getScriptTimeZone(), "MM/dd/yyyy HH:mm:ss"));
}
if (name == 'Active' && e.range.columnStart == 9 && e.value == "TRUE") {
let tsh = e.source.getSheetByName('Inactive');
tsh.appendRow(sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).getValues()[0]);
sh.deleteRow(e.range.rowStart())
} else if (name = 'Inactive' && e.range.columnStart == 9 && e.value == "FALSE") {
let tsh = e.source.getSheetByName('Active');
tsh.appendRow(sh.getRange(e.range.rowStart, 1, 1, sh.getLastColumn()).getValues()[0]);
sh.deleteRow(e.range.rowStart());
}
}
I am adding timestamp in col a if colb is edited on 3 sheets.
I have the trigger working onEdit and the information will be updated automatically. It works fine for one, but for 3 im getting an error and cannot run the script.
Is there a simpler way to look for edit in column b in three sheets than what I am doing?
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Igualdad Op Hom Mu") {
var r = s.getActiveCell();
if (r.getColumn() == 2) {
var nextCell = r.offset(0, -1);
if (nextCell.getValue() === '')
nextCell.setValue(new Date());
var s2 = SpreadsheetApp.getActiveSheet();
if (s2.getName() == "Incl Per Disc") {
var r2 = s2.getActiveCell();
if (r2.getColumn() == 2) {
var nextCell2 = r2.offset(0, -1);
if (nextCell2.getValue() === '')
nextCell2.setValue(new Date());
var s3 = SpreadsheetApp.getActiveSheet();
if (s3.getName() == "Empr y Col") {
var r3 = s3.getActiveCell();
if (r3.getColumn() == 2) {
var nextCell3 = r3.offset(0, -1);
if (nextCell3.getValue() === '')
nextCell3.setValue(new Date());
}
}
}
}
}
}
}
Try this:
function onEdit(e) {
const sh=e.range.getSheet();
const shts=['Igualdad Op Hom Mu','Incl Per Disc','Empr y Col']
if( shts.indexOf(sh.getName())!=-1 && e.range.columnStart==2) {
const ts=Utilities.formatDate(new Date,Session.getScriptTimeZone(),"yyyy/MM/dd HH:mm:ss");//time stamp
const rg=e.range.offset(0,-1);//next cell to the left
if(rg.getValue()=='') {
rg.setValue(ts);
}
}
}
This function requires the onedit trigger event object so you cannot run this script from the script editor unless you supply the event object.
In an 'onEdit' function, all examples that I find in this forum or elswhere contain the same (or similar) line to define the source, like:
if( r.getColumn() == 5 ) { //checks updates in column E
I cannot find how to adapt this, but I want my onEdit function to fire only if the column is set to a specific value:
if value of column E is set to "done" then perform rest of the function
Try something like this:
function onEdit(e) {
if (e.range.getColumn() === 5 && e.value.toLowerCase() === 'done')
Logger.log('Todo');
}
See Understanding Events.
I have solved this with following script:
function onEdit() {
var s = SpreadsheetApp.getActiveSheet();
if( s.getName() == "TaskBoard" ) { //checks that we're on the correct sheet; "TaskBoard"
var r = s.getActiveCell();
if( r.getColumn() == 2 ) { //checks updates in column B
var cellContent = r.getValue();
if (cellContent == 'Done')
{
var nextCell = r.offset(0, 4); //offset to same row, and column F; B+4
if( nextCell.getValue() === '' ) //is empty?
{
var time = new Date();
time = Utilities.formatDate(time, "CET", "yyyy-MM-dd");
nextCell.setValue(time);
}
}
};
};
}