Hello Stack Overlords!
I'm currently trying to put together a little coding project. Something simple, but I'm having a rather difficult time.
The objective, clear out three cells once the time of day, based on one of those cells, passes.
Essentially - G9 contains the the time I want the cells to be cleared. Once it has gone past the time of day specified in G9, I'd like cells D6, D9 and G9 cleared.
I've set up a way of doing this using an IF statement, both from the sheets side, and scripts side. On the sheet itself, I have a 1/0 toggle in cell K12. If the value in G9 is greater than the time now (stored in K11), it shouldn't be deleted, so is set as "1". As soon as the value in G9 is greater than the time now stored in K11, it set's the value to "0", meaning it should be deleted.
I've got Google Scripts to check the value of cell K12, and to delete my specified ranges if K12 = 0, as so:
function ClearCells() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('AutoDeleteCellsOnTimeExpired');
var Check = s.getRange("K12").getDisplayValue();
if (Check == "0"){
s.getRange('D6').clearContent();
s.getRange('D9:G9').clearContent();}
}
I've then set a toggle on the Scripts function, to run the script once a minute. The issue is, the script will not clear the cells, even after the value in K12 has changed from "1" to "0". The execution transcript shows the code executes fine.
Here's the clinch... If the value is 0, to begin with (I set a value in G9 already later than now), the script runs fine automatically, as expected. After taking a look, it seems that Google Sheets assumes because there hasn't been a USER edit to the sheet (the spreadsheet settings are set to re-calculate NOW formulas once a minute), it thinks the value can't be different and doesn't bother to re-check the value in K12. If I edit another cell on the spreadsheet (even totally unrelated), Google Scripts then checks the cell value again as expected.
This indicates to me it's because it's caching the value of K12, not expecting it to change without user input (which is wrong because it updates itself based on an automatic =NOW update)?
If I'm wrong, please let me know! Otherwise... how can I stop Google Sheets assuming this? I've tried adding a flush() command, but that did nothing.
Link to the spreadsheet can be found here (this is a disposable version, only showing the need-to-see info - edit and tweak away): https://docs.google.com/spreadsheets/d/1AGOLuvGCNWJk1Ft9T2ifjcf9CzCXNHysLiSvsUwxZW4/edit?usp=sharing
Many thanks for your time!
Your assumption
This indicates to me it's because it's caching the value of K12
is correct. The values are cached as to retrieve the data much more efficiently and quickly. The easiest way to refresh the cache is by making a dummy setValue() or clearContent() calls, before you retrieve the value using getValue(). Like so
function ClearCells() {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheetByName('AutoDeleteCellsOnTimeExpired');
s.getRange("A1").setValue("DummyValue") //Dummy Call
Logger.log("SetValue")
s.getRange("A1").clearContent() //Delete Dummy Value
// You can use either just clearContent() or setValue() and clearContent() calls
// To achieve the same result
var Check = s.getRange("K12").getValue();
Logger.log(Check)
if (Check == 0){
s.getRange('D6').clearContent();
s.getRange('D9:G9').clearContent();
}
Logger.log("New Version")
}
Alternatively, you can check the time value to the current time in the google app script directly and perform the required function, like so:
function ClearCells2() {
var s = SpreadsheetApp.getActiveSpreadsheet()
.getSheetByName('AutoDeleteCellsOnTimeExpired');
var curTime = new Date()
var expTime = s.getRange("G9").getValue().split(":")
Logger.log(curTime.getHours() +":"+curTime.getMinutes())
Logger.log(expTime[0]+":"+expTime[1])
if (curTime.getHours() >= expTime[0] && curTime.getMinutes() > expTime[1]){
Logger.log("Clear")
s.getRange('D6').clearContent();
s.getRange('D9:G9').clearContent();
}
}
References:
Date(),date.getHours(),date.getMinutes()
Related
A few days ago I got help from Stack Overflow to modify my then Apps Script code used to make calendar events from info on Google sheet, so as to tick a checkbox whenever an entry from the corresponding row is made and subsequently make new events only when the checkbox is unticked.
function addEvent() {
let webinarCalendar = CalendarApp.getCalendarById("blablablablablabla#gmail.com");
let calendarSheet = SpreadsheetApp.getActiveSheet();
let schedule = calendarSheet.getDataRange().getValues();
schedule.splice(0, 1);
const k = 16; // colIndex of checkbok col
const created = schedule.map(e => [e[k]]);
schedule.forEach(function(entry, i) {
if (entry[k] == true) { return; }
webinarCalendar.createEvent(entry[3], entry[14], entry[15], {description: entry[13]});
created[i][0] = true;
});
calendarSheet.getRange(2, k + 1, created.length, 1).setValues(created);
}
This current code worked just fine until 2 days ago when I updated 3 of the 4 cells with the required inputs to work on an array formula so that they get populated automatically whenever a new row entry is made.
The error on the app script console says :
Exception: The parameters (String,String,String,(class)) don't match the method signature for CalendarApp.Calendar.createEvent.
The parameters required for this createEvent() as per documentation are title(string), start time(string), finish time(string) and description(which is inside a javascript object I think and is also a string). To ensure that the datatype did not somehow get changed in the process of creating array formula, I cross checked the cells with an ISTEXT() and all of the inputs returned TRUE.
Second trial that I made was to change the splice() from (0,1) to (0,2) so that it ignores the first row which has the array formula written into the cells, which also did not fix the issue.
I would greatly appreciate if someone could show me what is causing this issue and help me fix it.
I don't know why it worked previously, but startTime and endTime should be Date.
I have checked that you columns are String.
Reference:
createEvent(title, startTime, endTime, options)
The error on the app script console says : Exception: The parameters (String,String,String,(class)) don't match the method signature for CalendarApp.Calendar.createEvent.
This is simply saying it's reading from data that is not in the proper data type. In this case, perhaps, try encasing the entries with 'new Date(entry[x])' for the respective start and end date/time entries.
For people trying to run the scripts, one underlying cause might be the fact that you may be using US locale when the date have been formatted as UK.
(e.g. Date that App Script is looking for is mm/dd/yyyy tt:tt:tt, but if you click in the formula cell it shows as dd/mm/yyyy tt:tt:tt)
What you would do is to go to Files > General > Locale > (Country of Choice) > Save settings.
You would then reload the page and try if the script is working now without that "Cannot find method createEvent(string,string,string)" error.
The line of code to use in your script would be:
SpreadsheetApp.getActive().setSpreadsheetLocale('en_UK');
You could include it in your onOpen trigger function.
This question already has answers here:
Refresh data retrieved by a custom function in Google Sheet
(21 answers)
Closed last year.
I do have simple custom function margin() in Google Sheet (using Apps Script), which takes a value from cell H55 in the sheet named "Exchange Rates" and simply returns the same value that is in the cell.
function margin()
{
var price = 0;
price = SpreadsheetApp.getActiveSpreadsheet().getRange("Exchange Rates!H55").getValue();
return price;
};
If I put =margin() to any cell (eg. H56) - it works. But when I update the value in cell H55, say... from the number 1,05 to 1,55 - nothing happens to the value of cell H56. (Where the formula equals margin() function).
When I put the formula =margin() in any cell it calculates correctly the first time, returning 1,55. (Again - when I change the value of cell H55 to another number, the cells where my formula is are not updated).
Please, do I have to somehow run the script again? Or what can I do? (I simply tried to refresh the page, but nothing happened and I definitely donĀ“t want to rewrite every cell with my function.) Thank you!
Alright, there are a couple of helpful answers here and here as per the comments.
But basically, if I understand correctly you desire your custom function margin() to get the value of a given cell (H55 in your code), ideally do something with it and return the result. (As it stands you are just returning the value of H55).
Why not have your custom function use H55 as parameter? This way when the value of H55 changes, all your occurrences of your custom function will be updated.
Example:
Sample script:
function margin(price){
// Return the margin (10%)
var margin = price*0.1;
return margin;
};
function timestamp() {
return new Date()
}
The function recalculates every-time i open the sheet. i only want it to recalculate on edit.
I have tried to mess with "onEdit" but i can never get it to work. I have also tried the "current project triggers" but that doesn't seem to fix the problem. I apologize that for the simplicity of the question, I simply can't find or figure out the answer.
Google sheets script
https://docs.google.com/spreadsheets/d/1-Ix9LUmMYNqWnz0mB2PAg9ocJ-TwszqHRbqxAchcxhc/edit#gid=0
It looks (based on your comments on the question), like you've created a custom formula and put it in a cell. If that's the case (meaning that you actually have =timestamp() in a cell), then it will always recalculate both on open and on edit.
That's just how formulas (both the standard ones and custom ones) work. You'll notice this when you have start to have too many formulas in a spreadsheet and it starts to slow down, or even not load correctly.
If you want something that only updates on edit, you'll have to write a function that does exactly that, then set a trigger that calls it on edit.
For example, this function will put the current date in cell A1 of the first sheet of the current spreadsheet:
function setTimestamp()
{
var thisSS = SpreadsheetApp.getActiveSpreadsheet();
var firstSheet = thisSS.getSheets()[0];
var firstCell = firstSheet.getRange(1, 1);
firstCell.setValue(new Date());
}
This kind of function will only run when you call it, so you can set the trigger to whatever you want. Obviously, you'll have to figure out how to select the cell where you want the date to appear.
Here is my problem:
I created this function on Google Script:
function LASTCOLELEMENT(colIndex) {
// Gets the whole SpreadSheet
var ss = SpreadsheetApp.getActiveSpreadsheet();
var registerSheet = ss.getSheetByName("Registro de Inventario");
// Returns the last element of the colIndex column
return registerSheet.getRange(registerSheet.getLastRow(), colIndex).getValue()
}
And I called it in my spreadsheet as:
=LASTCOLELEMENT(1)
The output is correct and it gives me what I want, but as soon as I add a row to "Registro de inventario" sheet, "=LASTCOLELEMENT(1)" still shows what was being showing before because it hasn't been called again.
If I delete "=LASTCOLELEMENT(1)" and re-write it again it shows the last element.
I've been thinking about it for a while and I don't know how to fix this.
Google sheet caches the result, and thus does not recall your function and does not refresh the data, because the formula parameter (1) does not change, so google inferes that the result does not change neither; eventhough the data that is being used does change, but that is beyond the google sheet inspection.
You might either get used (or educate your users) to make a manual refresh (with Ctrl-R)
Or force a data reload with a trick. Add an unused parameter to the function function lastColVal(colindex,fakecell) and pass a reference to a cell =LASTCOLVAL(1,$A$1). Eventhough your function does not use this value, its mere presence will make google sheet to refresh the data. Then, to force the refresh every time you add a row, change the value of the A1 cell, for example, with a COUNT of nonblank cells.
I have a function written in VBA that does what I want it to (see below. note: I did not write this function myself)
What I wanted was to a function, say, "=LastModifiedDateOf(CELL)", where CELL, is a parameter that indicates the cell I want to monitor. If the value of such cell ever gets changed, the cell containing the function has its value updated to the current date.
Ultimately I want a function written in google sheets that does the same thing, but I dont really understand how this function works.
Can anyone do one or both: 1) Explain whats happening in the function below, 2) Write a function in googlescripts (js) that does the same thing?
Public Function Lastmodified(c As Range)
Lastmodified = Now()
End Function
1) Explain whats happening in the function
Spreadsheet cell contents are recalculated only when the content of their references change. When you think about it for a bit, this makes sense for efficiency: it would be a waste of computer resources to calculate a new value for a cell when none of the referenced values (aka parameters) has changed... we'd just end up with the same value.
The magic for the VBA function you've provided is that the cell containing it gets recalculated only when the referenced cell is changed. That is under the control of the spreadsheet, and is completely independent of the script itself.
The same behavior is exhibited in Google Sheets. And in both cases, it's not important to the function what the monitored cell is - the function just returns the current time whenever it is called, which should be at times when the cell's value changes.
However, there are other reasons for the function to be called. In Excel, you can manually trigger spreadsheet recalculation, and that would update this function's output anywhere it appeared in the spreadsheet, with the current time. Likewise, in Google Sheets, any time you load the spreadsheet you could trigger a recalculation.
2) Write a function in googlescripts (js) that does the same thing
/**
* Return the time that the referenced cell or range was last changed.
* Initial use shows current time.
*
* #param {Sheet3!B32} reference Cell or range to monitor.
* #returns The time the reference was last changed.
* #customfunction
*/
function lastModified( reference ) {
// Surprise - we don't actually care what has changed!
return( new Date() );
}
In this snippet, if A1 cell is modified, the cell A2 is automatically updated with the modification date.
function onEdit(e) {
if (e.range.getA1Notation() === "A1") {
e.source.getRange("A2").setValue(new Date());
}
}