Can anyone help me do this?
My problem is that all of my three (3) codes has the same name "onEdit".
How can I run three (3) of of them?
There is a suggestion that I need to change their name, but when I change the names my codes wont run.
Can anyone post an example code for me, for my reference?
Please?
function onEdit() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
var lastRow = sheet.getLastRow();
for (p=1 ; p<=lastRow ; p++) { // p <= lastRow
var status = sheet.getRange("C"+p).getValue(); // Change P to the completed column
if (status == "no") { // status == "no"
sheet.hideRows(p);
}
}
}
function onEdit() {
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getActiveSheet();
var cell = spreadsheet.getActiveCell();
var col = cell.getColumn();
var row = cell.getRow();
var rows = [1, 2, 3];
// This is a list of the rows that should blink if edited.
if (col === 7 && rows.indexOf(row) !== -7 && sheet.getName() === 'Sheet1') {
// If the edited cell is in column A (1) and if the edited cell
// is one of the rows listed
for (var num = 0; num < 50; num++) {
var colour = num%2 === 0
? 'GOLD'
: 'WHITE';
// Using ? and : like this is called a ternary operation. It's a
// shorter form of if. ifStatement ? true : false.
sheet.getRange('G' + row + ':G' + row).setBackground(colour);
// Get the range for the edited row and set the bg colour
SpreadsheetApp.flush();
Utilities.sleep(500);
}
}
}
Two functions in one onEdit()
Here's an example that combines two functions into one onEdit() simple trigger.
function onEdit(e){
var ss=e.source;
var rg=e.range;
var sh=rg.getSheet();
var name=sh.getName();
Logger.log('Name: %s',name);
var includedSheets=['Sheet45','Sheet46']
if(includedSheets.indexOf(name)==-1){
return;
}
if(name=='Sheet45'){
Sheet45(e);//You could put the code right here but I wanted to make it clear that it's two different operation. This one deletes a row if all conditions are met.
}
if(name=='Sheet46'){
Sheet46(e);//This one deletes a column if all conditions are met
}
}
function Sheet45(e){//You can name them whatever you want
var sh=e.range.getSheet();
var row=e.range.getRow();
var col=e.range.getColumn();
Logger.log('Name: %s',e.range.getSheet().getName());
if(col==1 && row==4 && e.value=='delete'){//if column1 and row4 is changed to 'delete'
sh.deleteRow(row);//the it deletes row 4
}
}
function Sheet46(e){
var sh=e.range.getSheet();
var row=e.range.getRow();
var col=e.range.getColumn();
Logger.log('Name: %s',e.range.getSheet().getName());
if(row==1 && col==4 && e.value=='delete'){//if column4 and row1 is changed to 'delete'
var rg=sh.getDataRange();//then it deletes column 4
var vA=rg.getValues();
for(var i=0;i<vA.length;i++){
vA[i].splice(3,1);
Logger.log('vA[%s]: %s',i,vA[i]);
}
}
rg.clear();
sh.getRange(1,1,vA.length,vA[0].length).setValues(vA);
}
There are a lot of different ways to do it. I like to return as quickly as possible on sheets that are not involved in any of the functions.
Simple Triggers
Spreadsheet Documentation
You probably already know that you can't run these onEdit() functions directly unless you provide the event object.
Your two functions combined
I modified the second one a bit because it didn't make sense to me. So you change it since you probably understand what you want.
function onEdit(e){
oE1(e);
oE2(e);
}
function oE1(e) {//this works on all sheet
var ss=e.source;
var rg=e.range;
var sh=rg.getSheet();
var name=sh.getName();
var lastRow = sh.getLastRow();
for(var row=1;row<=lastRow;row++) {
var status=sh.getRange(row,3).getValue();//Column3 is C
if (typeof(status)=="string" && status.toLowerCase()=="no") {
sh.hideRows(row);
}
}
}
function oE2(e) {//this only works on sheet 1
var ss=e.source;
var rg=e.range
var sh=rg.getSheet();
var row=rg.getRow();
var col=rg.getColumn();
var cell=sh.getRange(row,col);
var rows = [1, 2, 3];
if(col==1 && rows.indexOf(row)!=-1 && sh.getName()=='Sheet1') {
sh.getRange(row,7).setBackground((rows.indexOf(row)%2==0)?'Gold':'White');
}
}
It would be nice to rework the organization so that you can return more quickly for sheets that are not involved with either function. It's really helpful to look at the executions page when debugging these functions.
Related
I have two working functions. One of them is myFunction with a trigger. Is protects the row of the cell when any information is entered in this cell in Column4.
function myFunction(e) {
const sheetNames = ['Sheet1', 'Sheet2', 'Sheet3']; // Please set the sheet names you want to run the script.
const range = e.range;
const sheet = range.getSheet();
const value = range.getValue();
const row = range.getRow();
if (!sheetNames.includes(sheet.getSheetName()) || range.getColumn() != 4 || row == 2 || value == "") return;
const p = sheet.getRange(`B${row}:D${row}`).protect();
const owner = Session.getActiveUser().getEmail();
p.getEditors().forEach(f => {
const email = f.getEmail();
if (email != owner) p.removeEditor(email);
});
}
Another function is an onEdit function. It adds date in Column1 when I enter information in Column4. The date appears in the same row with the cell in Column4.
function onEdit() {
var colToCheck = 4;
// Offset from the input [row, column]
var dateOffset = [0, -3];
// Sheets to proceed on
var sheetNames = ['Sheet1', 'Sheet2', 'Sheet3'];
var sheet = SpreadsheetApp.getActive().getActiveSheet();
var name = sheet.getName();
if (sheetNames.indexOf(name) > -1) {
var cell = sheet.getActiveCell();
var col = cell.getColumn();
if (col == colToCheck) {
var dateTimeCell = cell.offset(dateOffset[0], dateOffset[1]);
dateTimeCell.setValue(new Date());
}
}
}
How these two functions can be combined in one sheet?
I just added two separate functions to one sheet: the one with trigger and the one onEdit as two different codes. And they work as I need. So, we do not need to combine them somehow. They just work one after another. First, the one onEdit function works, and as it adds info to the necessary cell, the function with trigger starts working.
I am new to Google apps script (and javascript for that matter).
In the code below I keep getting "No logs found. Use Logger API to add logs to your project.", tried adding an "event" variable to the onEdit() trigger with no avail.
function onEdit() {
var test = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("test");
var activeCell = test.getActiveCell();
var col = activeCell.getColumn();
var row = activeCell.getRow();
Logger.Log(col);
Logger.Log(row);
if(col == 1 && row == 1) {
var testVar = 1;
Logger.log(testVar);
}
}
Thanks.
You can play around with this to learn more about the event object. It really has a lot of information in it right of out the box.
function onEdit(e) {
if(e.range.getSheet().getName()!='test'){return;}//keeps other pages from affecting the performance of the onEdit function
var activeCell = e.range;//typically e.range is one cell
var col = e.range.columnStart;//there's also a columnEnd
var row = e.range.rowStart;//there's also a rowEnd
Logger.log(JSON.stringify(e));//take a look at this and see what else is there
Logger.Log(col);
Logger.Log(row);
if(col == 1 && row == 1 && e.range.getSheet().getName()=='test') {
var testVar = 1;
Logger.log(testVar);
}
}
I have a set of data that is categorised by months every 3 columns. In other words, e.g. columns A to C is September, columns D to F is October and so on. I would like help on removing duplicates in each row for each month, so that if data in one row in columns A and B and C matches another row, the duplicate cells in that row is deleted. This is then repeated for the next month (next 3 columns D, E and F).
So far I've only managed to find scripts that remove duplicate rows in a specific range but not how to remove duplicate cells in rows and loop every few columns.
This is the code I have so far for removing duplicate rows in a specific range:
function deleteDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = "Master";
var sourceSheet = ss.getSheetByName(source);
var sourceRange = sourceSheet.getRange(3, 1, sourceSheet.getLastRow(), 3)
var sourceData = sourceRange.getValues();
var keepData = new Array();
var deleteCount = 0;
for(i in sourceData) {
var row = sourceData[i];
var duplicate = false;
for(j in keepData) {
if(row[0] == keepData[j][0] &&
row[1] == keepData[j][1] &&
row[2] == keepData[j][2]) {
duplicate = true;
}
}
if(!duplicate) {
keepData.push(row);
}
}
sourceRange.clear();
sourceSheet.getRange(3, 1, keepData.length,
keepData[0].length).setValues(keepData);
}
I'm pretty new to scripting so any help with modifying this script or new inputs is greatly appreciated. Thanks.
Update: Here's an example data in my google sheets if it makes it clearer.
https://docs.google.com/spreadsheets/d/1IDcBlzFj6992RvMXS3TOyqaS5HsSRqjxk5HXsnYrGkA/
Try this:
There's probably a lot of easier ways to do it. But I liked the problem so here's a way to do it. This example generates is own random data so you need to take that out before running it on your dataset. But if I pasted it correctly and you copy it correctly and make a Sheet2 for it to work on then it should run okay for you. If not, please be prepared to debug it on your own.
By the way it also has some extraneous notes and result so that I could figure out if it was working or not. Good Luck. Also I haven't tried it but I don't think this cares if the months appear in consecutive columns.
function removeDupesInGroups(){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet2');
generateContent(3,1,100,9);
var rg=sh.getRange(1,1,sh.getLastRow(),9);
var vA=rg.getValues();
var hdr={};
var dA=[];
for(var j=0;j<vA[0].length;j++){
if(!hdr.hasOwnProperty(vA[0][j])){
hdr[vA[0][j]]=[];
hdr[vA[0][j]].push(j);
}else{
hdr[vA[0][j]].push(j);
}
}
var n=0;
for(key in hdr){
var m=key;
var uA=[];
for(var i=2;i<vA.length;i++){
var tA=[];
for(var j=0;j<vA[i].length;j++){
var elements=hdr[key].join(',');
if(hdr[key].indexOf(j)>-1){
tA.push(vA[i][j]);
}
}
if(i<vA.length){
if(!isArrayContained(uA,tA)){
uA.push(tA);
}else{
sh.deleteRow(i - n + 1);
dA.push([Utilities.formatString('n=%s,i=%s,deleeting row %s,key=%s,group=%s',n,i,i-n+1,key,tA.join('|'))]);
sh.getRange(1, 10).setValue(Utilities.formatString('n=%s,i=%s,deleeting row %s,group=%s',n,i,i-n+1,tA.join(',')));
n++;
}
}
}
}
var resultrg=sh.getRange(sh.getLastRow()+2,1,dA.length,1).setValues(dA);
}
function isArrayContained(c,t){
for(var i=0;i<c.length;i++){
var isEqual=true;
for(var j=0;j<c[i].length;j++){
var t1=c[i][j];
var t2=t[j];
if(c[i][j]!=t[j]){
isEqual=false;
break;
}
}
if(isEqual){
return true;
}
}
return false;
}
function generateContent(row,col,rows,cols){
var ss=SpreadsheetApp.getActive();
var sh=ss.getSheetByName('Sheet2');
sh.getDataRange().clearContent();
sh.getRange(1,1,2,9).setValues([['Jan','Jan','Jan','Feb','Feb','Feb','Mar','Mar','Mar'],['Header','Header','Header','Header','Header','Header','Header','Header','Header']])
var rg=sh.getRange(row,col,rows,cols);
var vA=rg.getValues();
var s="For the Lamb on the Throne will be their Shepard.";
for(var i=0;i<vA.length;i++){
for(var j=0;j<vA[i].length;j++){
vA[i][j]=s.charAt(Math.floor(Math.random() * s.length+1));
}
}
rg.setValues(vA);
}
Not sure if this is the most efficient, but I tried this amendment to my code and it works for my purposes with test data. Here's the final code I used.
function deleteDuplicates() {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var source = "Master";
var sourceSheet = ss.getSheetByName(source);
for (var a=1; a<=19; a+=3){
var sourceRange = sourceSheet.getRange(3, a, sourceSheet.getLastRow(), 3)
var sourceData = sourceRange.getValues();
var keepData = new Array();
var deleteCount = 0;
for(i in sourceData) {
var row = sourceData[i];
var duplicate = false;
for(j in keepData) {
if(row[0] == keepData[j][0] &&
row[1] == keepData[j][1] &&
row[2] == keepData[j][2]) {
duplicate = true;
}
}
if(!duplicate) {
keepData.push(row);
}
}
sourceRange.clear();
sourceSheet.getRange(3, a, keepData.length,
keepData[0].length).setValues(keepData);
}
It basically starts at column a and adds 3 to it which changes the source range and loops until it reaches column 19.
I have a Spreadsheet with some functions. One of them is a onEdit(event) function that copies some values to other sheets based on conditions. This is the code (simplified but with the important parts intact):
function onEdit(event) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var s = event.source.getActiveSheet();
var r = event.range;
if(s.getName() === "Lista" && r.getColumn() === 9 && r.getValue() === "Posicionada") {
var sheetname = s.getRange(r.getRow(),3).getValue();
var columnRef = s.getRange(r.getRow(),4).getValue();
var row = s.getRange(r.getRow(),5).getValue();
var targetSheet = ss.getSheetByName("Mapa " + sheetname);
var headers = targetSheet.getRange(1, 1, 1, targetSheet.getLastColumn());
for (var i = 0; i < headers; i++) {
if (headers[i] === columnRef) {
break;
}
}
var column;
if (columnRef === "A1") {
column = 2;
}
else if (columnRef === "A2") {
column = 3;
}
else if (columnRef === "B1") {
column = 4;
}
else if (columnRef === "B2") {
column = 5;
}
if (sheetname === "N2") {
row = row - 30;
}
if (sheetname === "N3") {
column = column - 10;
row = row - 42;
}
targetSheet.getRange(row,column).setValue(s.getRange(r.getRow(), 1, 1, 1).getValue());
}
}
The code works as it should when I manually edit the cell. But, I have a code that edit the cell when the user press a button in a sidebar, this is the code:
function positionMU(){
var ss = SpreadsheetApp.getActiveSpreadsheet();
var cell = ss.getActiveCell().activate();
var cellLevel = cell.offset(0,2);
var cellLetter = cell.offset(0,3);
var cellNumber = cell.offset(0,4);
var cellStatus = cell.offset(0,8);
var dbq = "Posicionada";
var fora = "Pendente de recebimento";
if (cellStatus.getValue() == "Aguardando posicionamento"){
cellStatus.setValue(dbq); //attention in this line
}
else if (cellStatus.getValue() == "Aguardando saĆda"){
cellStatus.setValue(fora);
var cellExitDate = cell.offset(0,6);
cellExitDate.setValue(getDate());
}
}
As you can see, this function change the cell content with setValue(), but, when I use this function, the value of the cell changes, but the onEdit() trigger doesn't work.
How can I make the onEdit() trigger recognize changes made with setValue()?
You are right. onEdit() only triggers if the range is edited manually. As can be seen here, onEdit() triggers when a value is changed by the user.
I tested the function by making function to insert values into a column for which my onEdit responds and nothing happens. Including various other techniques that I could think of. Best thing to do here is to suggest this as an enhancement on App Script's Issue Tracker.
However, I made it work by writing another function to be called when another function in the script makes changes to the sheet. These are the test functions I wrote:
function addValues()
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var book = "Book";
var cancel = "Cancel";
var maxRow = range.getLastRow()+1;
for(var i=0; i<4; i++)
{
if (i%2 == 0)
{
sheet.getRange(maxRow, 1).setValue(book);
autoChanges(maxRow);
}else{
sheet.getRange(maxRow, 1).setValue(cancel);
autoChanges(maxRow);
}
maxRow++;
}
}
autoChanges function:
function autoChanges(row)
{
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getSheetByName("Sheet1");
var range = sheet.getDataRange();
var data = range.getValues();
var response = "";
sheet.getRange(row, 2).protect();
response = data[row-1][0];
if (response == "Book")
{
sheet.getRange(row, 2).canEdit();
}else{
sheet.getRange(row, 2).setValue("--NA--");
}
}
Not the most elegant solution but this seems to be the only workaround for what you are trying to do.
There are some very good reasons why calling range.setValue() doesn't trigger the onEdit event, and most of them have to do with infinite recursion. In fact, you call setValue() yourself WITHIN onEdit(). This would trigger a recursive call, and from what I can see, you have no provision for handling the base case and thus your code would explode if setValue() did what you want.
Why not simply take all of your code out of your event handler, and put it into another function:
function onEdit (e) {
return handleEdits(e.range);
}
function handleEdits(r) {
s = r.getSheet();
ss = s.getParent();
//the rest of your code should drop right in.
}
then, inside your autoChanges function, go ahead and call handleEdits, passing it an appropriate range, after your call to setValue().
If you like to play with fire, and I personally do, you can call the onEdit(e) function after you make a change. Just send in an object whatever is called and used by the e object.
For me, I just needed to add:
var e={};
e.range=the range you are making a change to in the script;
e.source = SpreadsheetApp.getActiveSpreadsheet();//or make sure you have the sheet for wherever you are setting the value
e.value=whatever value you are setting
e.oldValue=if you onEdit needs this, set it here
//if you are using any of the other standard or special properties called by your onEdit...just add them before calling the function.
onEdit(e);//call the function and give it what it needs.
This question already has an answer here:
Merging or Combining two onEdit trigger functions
(1 answer)
Closed 1 year ago.
I got two onEdit() function scrips on a Google spreadsheet. But seems like one function is working at a time.
First function is a script to color all Rows, and second one is date function for adding dates on 2 cells based on column edit.
Here are the scripts.
function colorAll()
{
var sheet = SpreadsheetApp.getActiveSheet();
var startRow = 4;
var endRow = sheet.getLastRow();
for (var r = startRow; r <= endRow; r++) {
colorRow(r);
}
}
SpreadsheetApp.flush();
function colorRow(r)
{
var sheet = SpreadsheetApp.getActiveSheet();
var dataRange = sheet.getRange(r, 1, 1, 32);
var data = dataRange.getValues();
var row = data[0];
SpreadsheetApp.flush();
if(row[14] === ""){
dataRange.setBackgroundRGB(255, 255, 255);
dataRange.setFontColor("#000000");
}
else if(row[14] === "BEING USED") {
dataRange.setBackgroundRGB(150, 185, 255);
dataRange.setFontColor("#004BE1");
}
}
function onEdit(event)
{
var r = event.source.getActiveRange().getRowIndex();
if (r >= 2) {
colorRow(r);
}
}
function onOpen(){
colorAll();
And the second function.
function onEdit(e) {
var aCell = e.source.getActiveCell(), col = aCell.getColumn();
if(col == 19 || col == 21) {
var adjacentCell = aCell.offset(0, 1);
var newDate = Utilities.formatDate(new Date(),
"GMT+1", "dd/MM/yyyy");
adjacentCell.setValue(newDate);
}
}
The date function is working but the colorRow function is not, if I remove the date script then the colorRow will work.
Can any one point me in the right direction? Seems like I am missing something
Thanks
Barry Smith's comment about "two functions with same name" is right; only the second would execute in that case.
You can have just one spreadsheet-contained function named onEdit(). If you want to use another function as an onEdit trigger, you need to set it up as an installable trigger.
You can use the dialog from Resources -> Current Project's Triggers to install the second trigger.
Alternatively, you could have just one onEdit() simple trigger, but have it call the "sub-trigger" functions, passing the event object e to each of them.
Background: Guide to Triggers.
I might be misunderstanding, but it looks like you have two different functions with the exact same name. This can't happen, because the second one is basically overwriting the first one. Give the different names and it should work.
Found a very simple solution for this, just add the below to the start of your script:
function onEdit(e){
onEdit1(e);
onEdit2(e)
}
I rename my onEdit accordingly e.g. instead of onEdit1(e) I'll use hideRow(e) or whatever is relevant.
Here's a full code using three onEdit functions (automatically adding time to an edited cell, auto hiding a row when using a tick box and auto inserting a row):
function onEdit(e) {
autoTime(e);
hideRow(e)
}
function autoTime(e) {
var s = SpreadsheetApp.getActiveSheet();
if (s.getName() == "Sheet1") {
var r = s.getActiveCell();
if (r.getColumn() == 2) {
var nextCell = r.offset(0, 1);
if (nextCell.getValue() === '')
nextCell.setValue(new Date());
}
}
}
function hideRow(e) {
var ss = SpreadsheetApp.getActiveSpreadsheet();
var sheet = ss.getActiveSheet();
if (e.range.columnStart != 17 || e.value != "TRUE") return;
SpreadsheetApp.getActiveSheet().hideRows(e.range.rowStart);
}
function insertRow(e) {
var sheet = e.range.getSheet();
var lastRow = sheet.getLastRow();
if (e.range.getRow() > lastRow - 5)
sheet.insertRowAfter(lastRow - 1);
}