Date stamp script not working with arrayformula - google-apps-script

I'm trying to use a simple date stamp script in Google Sheets that will input today's date upon edit of another column but then will not change thereafter. I also have an array formula set on the column the script is looking at for the edits and it appears the array formula is preventing the script from working properly. Here is the formula:
={"Triggered";arrayformula(IFS(E2:E="","",C2:C>=E2:E,"✅",TRUE,""))}
And this is the script:
function onEdit(e) {
var sh = e.source.getActiveSheet();
if(sh.getName() !== "Watchlist" || e.range.columnStart !== 8) return;// 1 is A, 2 is B, 3 is C, etc
var o = e.range.offset(0, 1)
if(!o.getValue()) o.setValue(new Date())
}
Any help with what I'm doing wrong is appreciated. Thanks.

Unfortunately, according to the official documentation :
An installable edit trigger runs when a user modifies a value in a
spreadsheet.
Even when the output of the arrayformula is updated, the cell still contains the same formula. Therefore, the onEdit trigger won't fire since there wasn't any edit action by the user.
Read also the discussion here.

Related

Modifying e.range.getA1Notation() to accept wider range of inputs in Google Apps Script

I'm using a Google Apps script to update the active sheet's name with a cell (C6) value when another cell (I16) is updated. Currently, the script works fine when I16 is updated and the sheet is renamed as intended.
However, I would like the range of inputs to be much wider, like an A:J kind of range, or at least A1:J104. How can I achieve this?
I have also read this thread but I'm unsure of how to extract that info specific to this scenario.
The script I'm using is as follows:
function onEdit(e) {
if(e.range.getA1Notation() !== 'I16') return;
var s = e.source.getActiveSheet();
s = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().setName(s.getRange('C6').getValue());
}
Any assistance will be much appreciated. Thank you in advance!
EDIT: With the script, the following happens:
Upon editing cell I16, the contents of cell C6 changes via a formula. For example, when I enter "002" into cell I16, cell C6 updates to "Report-002", and so on.
At the same time, the script extracts the string in cell C6 and updates the name of the current sheet accordingly (i.e. the sheet is now named "Report-002".
The script works well for this specific scenario. However, I would like to trigger the script when I update a wider range of cells, and not just cell I16. Ideally, I'd like the script to run if I updated any cell in the sheet.
Does that help to clarify what I'm trying to achieve?
Try
function onEdit(e) {
e.source.getActiveSheet().setName(e.source.getActiveSheet().getRange('C6').getValue());
}
Change the if condition to account for bounds:
const [/*actual row*/ar, ac] = ['rangeStart','columnStart'].map(s => e.range[s])
const [/*row start*/rs, re, cs, ce] = [1,2,3,4];//C1:D2
if(ar >= rs && ar <= re && ac >= cs && ac <= ce){
// do stuff
}

Google App script trigger does not work on simple custom function

I am very new to google app script while having some experience both in javascript and python.
I am trying to run a very simple script in this custom spreadsheet, see row 1.
I am using the custom function feature, no apps/services deployed and I have full read/write authorizations.
I am developing some time conversion and duration calculation functions for a personal project, while trying to experiment with triggers starting with very simple functions.
The code associated with the trigger is indeed very simple: check the value of a predefined cell ("A1" in this case) and return its value in the cell where the function is invoked. As simple as possible, just to experiment with app script triggers:
function zain(){
var sheet = SpreadsheetApp.getActiveSheet();
var a = sheet.getRange("A1").getValue();
return a;}
To the effect of updating the cell value whenever any cell in the sheet is changed, I installed an onEdit() trigger on the function "zain()".
Now, the problem is: it does not work. The trigger log shows that onEdit() is successfully being completed, but the value in the cell where the function is invoked does not change, at all. It only seems to work whenever I re-paste the function or change the code and save it. Otherwise nothing.
I looked through all possible issues related to triggers in this forum and, contrarily to other cases, I could not find any working solution. I checked issues with trigger objects, spreadsheet access, but none of these solutions seems to be working/apply to my case.
If someone could be so kind to help in this matter, it would be greatly appreciated.
Here is a onEdit(e) function that takes Celsius from A1 and calculates Fahrenheit in B1
function onEdit(e) {
const sh = e.range.getSheet();
if(sh.getName() == 'Sheet0' && e.range.columnStart == 1 && e.range.rowStart == 1 && e.value ) {
e.range.offset(0,1).setValue(e.value * (9 / 5) + 32);
}
}
function zain(a) {
return (a - 32) * 5 / 9;
}
in C1 put "=zain(B1)" then when B1 changes the value of A1 should end up in C1 but not because of the onEdit trigger it's because B1 has changed
Demo:

How to get google sheets to reorder based on ranking

I have created a Google Sheet that has a "days since" rank based on differences from todays date to date of the last "incident".
These days update automatically, but what I want is to have a ranking system where the Google Sheet reorders if the date of someone's last incident changes.
Is this possible? I have attached the link below, but it is a very simple formula as of now.
Row D: =today()-C3
https://docs.google.com/spreadsheets/d/12SUfrB7XuqGlfxjvaePHJTGaZz_FhjiWhLxjPMAM2jI/edit?usp=sharing
If you don't want to use Google Apps Script you may duplicate this table (in sheet2 for example) and have it's content duplicated there within sort() function.
Lets say you put
=sort(Sheet1!A3:D8,4,0) in A3 cell of new Sheet2
I know it's not exactly the goal...
I believe your goal as follows.
You want to automatically sort the data with the column "C" when users edited the cells "C3:C8".
The table of the data is the cells "A3:D8".
From your tag of google-apps-script, I thought that your direction includes to use Google Apps Script.
In this case, I would like to propose to use OnEdit simple trigger for achieving your goal. The sample script is as follows
Sample script:
Please copy and paste the following script to the container-bound script of the Spreadsheet and save it.
function onEdit(e) {
const range = e.range;
const sheet = range.getSheet();
if (sheet.getSheetName() == "Sheet1" && range.getRow() >= 3 && range.getRow() <= 8 && range.getColumn() == 3) {
sheet.getRange("A3:D8").sort({column: 3, ascending: true});
}
}
When you edit the cells "C3:C8", the script is automatically run by the OnEdit simple trigger, and the data is sorted with the column "C".
Result:
When your shared Spreadsheet is used with above sample script, the following result can be obtained.
Note:
When you want to sort with the column "D", please modify above script as follows.
From
sheet.getRange("A3:D8").sort({column: 3, ascending: true});
To
sheet.getRange("A3:D8").sort({column: 4, ascending: false});
References:
Simple Triggers
sort(sortSpecObj)

Google Sheets auto populate timestamp in a cell when a particular value is entered in any other cell

I'm using work tracker in Google Sheets. I have columns from A to U. I have "Status" in "L" column with drop down options - Inquiry Status Pending, Reconciliation Done, Waiting for IM Response. When I set the Status to "Inquiry Status Pending" I want timestamp to be populated in "N" column and it should be static. Like wise - for Reconciliation Done, timestamp should be in "E" column. For "Waiting for IM Response" status, timestamp should be in "R" column. All timestamps should be static
The best way to insert a timestamp on edit event is a Google Apps Script
Apps Script offers many useful functions to work with Google spreadsheets. It also contains triggers like onEdit that fire when an edit takes place in your spreadsheet and allow you retrieve the event objects (useful for determining e.g. the edited column). The best way to become familiar with Apps Script is following the official tutorial.
How to implement your request in Apps Script:
Go from your spreadsheet on Tools -> Script editor
Paste the script bellow
Save the script and click on the "play button" (to run the script)
Ignore the error message you receive. You script now works correctly.
function onEdit(e) {
var range = e.range;
var column = range.getColumn();
Logger.log('edited');
if (column == 12){
var timestamp = new Date();
var row = range.getRow();
var value = e.value;
var sheet = e.source.getActiveSheet();
Logger.log("edited cell: "+range.getA1Notation());
if( value == "Inquiry Status Pending"){
sheet.getRange(row, 14).setValue(timestamp);
} else if(value == "Reconciliation Done"){
sheet.getRange(row, 5).setValue(timestamp);
} else if(value == "Waiting for IM Response"){
sheet.getRange(row, 18).setValue(timestamp);
}
}
}
I recommend you to spend some time to study Apps Script so you understand how the provided code works and can adapt it to your needs.

Custom function won't refresh as inputs are changed

I have a custom function that finds the value of another cell and displays it. When the source cell is changed, the function does not reflect.
https://docs.google.com/spreadsheets/d/1wfFe__g0VdXGAAaPthuhmWQo3A2nQtSVUhfGBt6aIQ0/edit?usp=sharing
Refreshing google sheets
function findRate() {
var accountName = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(1,1).getValue(); //determine the account name to use in the horizontal search
var rateTab = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('Rates'); //hold the name of the rate tab for further dissection
var rateNumColumns =rateTab.getLastColumn(); //count the number of columns on the rate tab so we can later create an array
var rateNumRows = rateTab.getLastRow(); //count the number of rows on the rate tab so we can create an array
var rateSheet = rateTab.getRange(1,1,rateNumRows,rateNumColumns).getValues(); //create an array based on the number of rows & columns on the rate tab
var currentRow = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getActiveCell().getRow(); //gets the current row so we can get the name of the rate to search
var rateToSearch = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet().getRange(currentRow,1).getValue(); //gets the name of the rate to search on the rates tab
for(rr=0;rr<rateSheet.length;++rr){
if (rateSheet[rr][0]==rateToSearch){break} ;// if we find the name of the
}
for(cc=0;cc<rateNumColumns;++cc){
if (rateSheet[0][cc]==accountName){break};
}
var rate = rateSheet[rr][cc] ; //the value of the rate as specified by rate name and account name
return rate;
}
If I change a rate in the rate tab, I need the custom function to recognize the new rate and update its value
You want to recalculate the custom function of =findRate(), when the cells of the sheet name of Rates are edited.
If my understanding is correct, how about adding the following sample script? Please think of this as just one of several answers.
Solution:
In order to recalculate the custom function, in this answer, the formula of =findRate() is overwritten by the script running with the OnEdit event trigger (in this case, it's the simple trigger.). By this, the recalculate is executed. But, when the formula is directly replaced by the same formula, the recalculate is not executed. So I used the following flow.
Retrieve all ranges of cells which have the formula of =findRate() from the sheet of "Projected Revenue".
Clear the formulas of the ranges.
Put the formulas to the ranges.
By this flow, when the cell of the sheet of "Rates" is edited, the custom function of =findRate() is recalculated by automatically running onEdit().
Sample script:
Please copy and paste the following script to the script editor. Then, please edit the cells of sheet name of Rates. By this, onEdit() is automatically run by the OnEdit event trigger.
function onEdit(e) {
var range = e.range;
if (range.getSheet().getSheetName() == "Rates" && range.rowStart > 1 && range.columnStart > 1) {
var sheetName = "Projected Revenue"; // If you want to change the sheet name, please modify this.
var formula = "=findRate()";// If you want to change the function name, please modify this.
var sheet = e.source.getSheetByName(sheetName);
var ranges = sheet.createTextFinder(formula).matchFormulaText(true).findAll().map(function(e) {return e.getA1Notation()});
sheet.getRangeList(ranges).clearContent();
SpreadsheetApp.flush();
sheet.getRangeList(ranges).setFormula(formula);
}
}
Note:
onEdit(e) is run by the OnEdit event trigger. So when you directly run onEdit(e), an error occurs. Please be careful this.
In this sample script, as a sample, even when the row 1 and column "A" of the sheet of "Rates" are edited, the custom function is not recalculated. If you want to modify this and give the limitation of range you want to edit, please modify the above script.
References:
Simple Triggers
Class TextFinder
Class RangeList
flush()
If I misunderstood your question and this was not the result you want, I apologize.
Added:
The proposal from TheMaster's comment was reflected to the script. When sheet.createTextFinder(formula).matchFormulaText(true).replaceAllWith(formula) can be used, also I think that the process cost will be much reduced. But in my environment, it seemed that the formulas are required to be cleared once to refresh the custom function, even if flush() is used. So I have proposed above flow.
But, now I could notice a workaround using replaceAllWith() of TextFinder. So I would like to add it. The flow of this workaround is as follows.
Replace all values of =findRate() to a value in the sheet of Projected Revenue using replaceAllWith()..
In this case, as a test case, the formulas are replaced to sample.
Replace sample to =findRate() using replaceAllWith().
By this flow, I could confirm that =findRate() is recalculated. And also, it seems that flush() is not required for this situation.
Sample script:
Please copy and paste the following script to the script editor. Then, please edit the cells of sheet name of Rates. By this, onEdit() is automatically run by the OnEdit event trigger.
function onEdit(e) {
var range = e.range;
if (range.getSheet().getSheetName() == "Rates" && range.rowStart > 1 && range.columnStart > 1) {
var sheetName = "Projected Revenue";
var formula = "=findRate()";
var tempValue = "sample";
var sheet = e.source.getSheetByName(sheetName);
sheet.createTextFinder(formula).matchFormulaText(true).replaceAllWith(tempValue);
sheet.createTextFinder(tempValue).matchFormulaText(true).replaceAllWith(formula);
}
}