Question

I have a Google spreadsheet of timesheet data; it has a sheet for each month, each sheet is a lot of six column blocks, one block per client.

I have created a summary sheet that goes and gets the total for each clients and displays it in a list:

function getClientTotals(sheetname, colcount)
{  
  colcount = colcount ? colcount : 6;
  var res;      
  var ss = SpreadsheetApp.openById('myid_goes_here');
  if(ss)
  {
    res = [];
    var totrow = ss.getRange(sheetname + '!A1:ZZ1').getValues()[0];
    for(var i = 0; i < totrow.length; i += colcount)
    {
      res.push([totrow[i], totrow[i + colcount - 1]]);
    }
  }   
  return res;
}

I have then just added a cell to my summary sheet containing =getClientTotals($C$7,$C$8) which passes in the sheet name for the month and the number of columns for each client (in case of "schema" modifications.

This all works fine, however, it does not update when the source data is changed. I have added an onEdit trigger; no joy. It updates if you go to the script editor and hit Save, but that's not useful. Am I missing something?

Was it helpful?

Solution

You're missing the fastidious caching bug feature. It works this way:

Google considers that all your custom functions depend only on their parameters values directly to return their result (you can optionally depend on other static data).

Given this prerequisite they can evaluate your functions only when a parameter changes. e.g.

Let's suppose we have the text "10" on cell B1, then on some other cell we type =myFunction(B1)

myFunction will be evaluated and its result retrieved. Then if you change cell B1 value to "35", custom will be re-evaluated as expected and the new result retrieved normally. Now, if you change cell B1 again to the original "10", there's no re-evaluation, the original result is retrieved immediately from cache.

So, when you use the sheet name as a parameter to fetch it dynamically and return the result, you're breaking the caching rule.

Unfortunately, you can't have custom functions without this amazing feature. So you'll have to either change it to receive the values directly, instead of the sheet name, or do not use a custom function. For example, you could have a parameter on your script telling where the summaries should go and have an onEdit update them whenever a total changes.

OTHER TIPS

Use a google finance function as a parameter. Like =GOOGLEFINANCE("CURRENCY:CADARS")

Those function force reload every x minutes

I use a dummy variable in a function, this variable refers to a cell in the spreadsheet. Then I have a Myfunction() in script that writes a Math.Random number in that cell.

MyFunction is under a trigger service (Edit/Current Project Triggers) and you can choose different event-triggers, for example On-Open or time driven, there you can choose for example a time period, from 1 minute to a month.

another solution to the caching problem.

have a dummy variable in your method. pass

Filter(<the cell or cell range>,1=1)

as the value to that parameter.

e.g.

=getValueScript("B1","B4:Z10", filter(B4:Z10,1=1))

the output of filter is not used. however it indicates to the spreadsheet that this formula is sensitive to B4:Z10 range.

I had a similar issue creating a dashboard for work. Chamil's solution above (namely using Sheet's Filter function passed as the value to a dummy variable in your function) works just fine, despite the more recent comment from Arsen. In my case, I was using a function to monitor a range and could not use the filter on the same range since it created a circular reference. So I just had a cell (in my case E45 in the code below) in which I changed the number anytime I wanted my function to update:

=myFunction("E3:E43","D44",filter(E45,1=1))

As Chamil indicated, the filter is not used in the script:

function myFunction(range, colorRef, dummy) {
  variable 'dummy' not used in code here
}

What you could do is to set up another cell somewhere in the spreadsheet that will be updated every time a new sheet is added. Make sure it doesn't update for every change but only when you want to do the calculation (in your case when you add a sheet). You then pass the reference to this cell to your custom function. As mentioned the custom function can ignore this parameter.

Given that feature explained by Henrique Abreu, you may try the out-of-box spreadsheet function QUERY , that SQL liked query is what I use often in work on raw data, and get data as summary to a different tab, result data is updated in real time following change in raw data.

My suggestion is based on the fact that your script has not advanced work such as URL fetch, just data work, as without actual data read, I cannot give a precise solution with QUERY.

Regarding the cache feature mentioned by Henrique Abreu (I don't have enough reputation to comment directly under his answer), I did testing and found that:

  1. looks there is no cache working, testing function's script shown below:

    function adder(base) { Utilities.sleep(5000); return base + 10; }

applying that custom function adder() in sheet by calling a cell, and then changed that cell value forth and back, each time I see the loading message and total time more than 5 seconds. It might be related to the update mentioned in this GAS issue:

This issue has now been fixed. Custom functions in New Sheets are now context aware and do not cache values as aggressively.

  1. the issue mentioned in this topic remains, my testing suggests that, Google sheet recalculate custom function each time ONLY WHEN

    • value DIRECTLY called by function is changed.

    function getCellValue(sheetName,row,col) { var ss= SpreadsheetApp.getActiveSpreadsheet(); var sh = ss.getSheetByName(sheetName); return sh.getRange(row, col).getValue(); }

    enter image description here
    A change of any value in yellow cells will lead to recalculation of custom function; the real data source value change is ignored by function.

    • function containing cell's location is changed in sheet. ex. insert/remove a row/column above or left side.

I did not want to have a dummy parameter. YMMV on this.

1 A cell that is a 'List of Items', one is "Refresh"

2 Script with 'onEdit', if the cell is "Refresh":

a)Empty out the document cache

b)Fill doc cache with external data (a table in my case)

c)For all cells with my 'getStockoData(...' custom function

  • get the formula

  • set '=0'

  • set the fromula

d)Set the cell in (1) with a value of "Ready"

This does refresh the bits you want BUT IS NOT FAST.

Since google app script is an extension of JS, functions should be able to handle more args than defined in function signature or fewer. So if you have some function like

function ADD(a, b) {
  return CONSTANTS!$A$1 + a + b
}

then you'd call this func like

=ADD(A1, B1, $A$2)

where $A$2 is some checkbox (insert -> checkbox) that you can click to "refresh" after you needed to change the value from the sheet & cell CONSTANTS$A$1

As @Brionius said put an extra dinamic argument on the function. if you use now() you may have timeout problems make the update a little bit slower...

cell A1 = int(now()*1000)
cell A2 = function(args..., A1)
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top