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());
}
}
Related
I'm using a custom function to pull price data for EVE Online and I would only like the function to update upon a specific cell change. Instead it's updating every ~10-20 minutes which is using up a ton of my daily quota for URL Fetches.
Looking below you can see the main function is in a page labeled "Warehouse Stock". It pulls all price data for items in the "Item" column. The cell refreshes with a custom function that changes cell B4 in the utility sheet. When that cell changes it updates the values in the custom function because of the IF/THEN statement. Lastly the prices are loaded into the main sheet and sorted by the "product" column.
Also I have recalculation "On Change" and Iterative Calculation turned off.
Thank you for reading and any help you can provide.
The custom function is:
/**
* Query's Fuzz market API for the given types
* #param {range} A vertical range of type_ids.
* #return maxBuy and minSell for each type_id
* #customfunction
*/
function fuzzApiPriceData(type_ids) {
if (!type_ids) throw 'type_ids is required';
const ids = Array.isArray(type_ids) ? type_ids.map(id => id[0]) : [type_ids];
const fuzz_price_data = JSON.parse(UrlFetchApp.fetch(`https://market.fuzzwork.co.uk/aggregates/?station=60008494&types=${ids.join(',')}`));
return [['minSell', 'maxBuy']].concat(type_ids.map(type_id => [parseFloat(fuzz_price_data[type_id]['sell']['min']), parseFloat(fuzz_price_data[type_id]['buy']['max'])]));
}
i think there's a fairly basic misunderstanding here.
"Custom functions" are designed to be written in cells on a sheet. they are by there nature designed to run ALL the time, constantly updating.
If you are attempting to run a custom script, you do that from the script editor itself, you don't even write the function in a cell at ALL.
I'm assuming you didn't write the "custom function" you're using(?) So it will be a bit difficult (but not impossible!) to convert it to a script that writes to a certain place on the sheet at a certain time. But you might research a bit more about the difference between custom functions designed to be executed in cells, and scripts designed to be run on a time trigger.
Regarding you answer:
I would like the function to refresh everyday, aswell as having the option to manually refresh the function by the contents of the cell Utility!B4
I propose you to use Installable Triggers:
Time-driven trigger: It can execute a function each day at specific time.
Installable edit trigger: It can execute a function when a certain value in a specific cell changes.
However, if you want to execute a function manually, I recommend you to insert a drawing and assign a function to it.
Reference
Installable Triggers
Clickable images and drawings in Google Sheets
What I usually do for that is : put a checkbox in a cell. Then, when calling your custom function, add an extra reference to that cell, even if the parameter is not included in your custom function. Each time you will switch true/false, your custom function will be reactivated. Then you can also define another function with a trigger that just change the value of true/false. Here is an example in sheet 'Data' cell A3 : the function is getDataJSON(C1,A2:U2,A1) with A1 even if the custom function take into account only the first and second parameter. https://docs.google.com/spreadsheets/d/1DN0Gfim0LC098zVgrUpt2crPWUn4pWfZnCpuuL1ZiMs/edit?usp=sharing
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;
};
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()
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.