Apps Script: Turn Google Sheet Formula Into A Callback Function - google-apps-script

I have these 2 columns in my sheet: A and B.
A contains dates in format day.month.year. B contains hours in format hh:mm. I usually concatenate the values of these 2 columns by using this formula:
=concatenate((text(A3;"mm.dd.yyyy")&" "&text(B3;"hh:mm")))
I use the same formula to concatenate the values of 2 other cells: C (filled with dates!) and D (filled with hours!). The exact formula in this case is:
=concatenate((text(C3;"mm.dd.yyyy")&" "&text(D3;"hh:mm")))
The whole purpose of this little exercise is to get the dates and hours in the format that is required to create events in my Google Calendar. I am currently using it in my calendar event creation script with the method setFormulaR1C1(formula), but I am not sure if that´s a really good idea. So, please tell me:
How would an apps script equivalent of my formula look like?
And is there a way to write that apps script equivalent of my formula as a callback function?
Thank you so much in advance!

How would an apps script equivalent of my formula look like?
Given that the cells already display the date and time in the format you need, you can simply use this:
const sheet = SpreadsheetApp.getActive().getSheetByName('Sheet1');
const datetimeString =
sheet.getRange('A3').getDisplayValue()
+ ' '
+ sheet.getRange('B3').getDisplayValue();
If the cells do not display the date and time in the required format, you will need to use Utilities.formatDate() and Spreadsheet.getSpreadsheetTimeZone(). There are some complications when interpreting spreadsheet time values in Apps Script. Refer to the recipe at Google Script when taking time from sheets adds +1 minute to avoid surprises.
To get the same with a spreadsheet formula more easily, use this:
=trim(A3) & " " & trim(B3)

Related

How to create INDIRECT array string of multiple sheet references in Google Sheets?

I am attempting to use a query to display data off multiple Google Sheets. I make a new sheet every week that has a specific sheet name, e.g. Week of 01/13, Week of 01/06 and so forth.
The following is where my idea spawned from for reference:
I have a summary sheet that is using COUNTA(INDIRECT("'" & A5 &
"'!E4:E",true)
A5 being a cell that concatenates a date and words to replicate the
sheet names.
The row on the summary sheet does not populate until B5<=today()
So I am able to set it an forget it and the sheet will continue to
give me my weekly data as the days progress and keeps the sheets clean
until the week is upon us.
Long story short, I have a query that I use that gives me all the data I need with a specific parameter but I have to manually update the data syntax array with the new sheet names each week.
=QUERY({'Week of 01/13'!A:P;'Week of 01/06'!A:P;'Week of 12/30'!A:P;'Week of 12/23'!A:P;'WEEK OF 12/16'!A:P;'WEEK OF 12/09'!A:P;'WEEK OF 12/02'!A:P;'WEEK OF 11/25'!A:P;'WEEK OF 11/18'!A:P;'WEEK OF 11/11'!A:P;'WEEK OF 11/04'!A:P;'WEEK OF 10/28'!A:P;'WEEK OF 10/21'!A:P;'WEEK OF 10/14'!A:P;'WEEK OF 10/07'!A:P;'WEEK OF 09/30'!A:P;'WEEK OF 09/23'!A:P;'WEEK OF 09/16'!A:P;'WEEK OF 09/09'!A:P;'WEEK OF 09/02'!A:P},
"Select * where Col11 = 'RD' order by Col2 desc",0)
I would like to build a reference to an array that will auto-populate a concatenation based on the day.
Using the following code I can have the concatenate give me the array I need,
=if(H4<=today(),CONCATENATE("'",H$1,text(H4,"mm/dd"),"'!A:P;",),"")
but when I try to input it into the query function it just returns the concatenated text:
=QUERY(I1,"Select *")
'Week of 01/06'!A:P;'Week of 01/13'!A:P
I have tried with and without the curly brackets with no success.
I would like the sheet to be able to refresh and see that it is the correct day, the new sheet name is populated and the query gets updated.
I need help with making I1 work.
Link to Test Query Sheet
dudes who copy-pasted INDIRECT function into Google Sheets completely failed to understand the potential of it and therefore they made zero effort to improve upon it and cover the obvious logic which is crucial in this age of arrays.
in other words, INDIRECT can't intake more than one array:
=INDIRECT("Sheet1!A:B"; "Sheet2!A:B")
nor convert an arrayed string into active reference, which means that any attempt of concatenation is also futile:
=INDIRECT(MasterSheet!A1:A10)
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{Sheet1!A:B; Sheet2!A:B}")
————————————————————————————————————————————————————————————————————————————————————
={INDIRECT("Sheet1!A:B"; "Sheet2!A:B")}
————————————————————————————————————————————————————————————————————————————————————
=INDIRECT("{INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}")
the only possible way is to use INDIRECT for each end every range like:
={INDIRECT("Sheet1!A:B"); INDIRECT("Sheet2!A:B")}
which means that the best you can do is to pre-program your array like this if only part of the sheets/tabs is existant (let's have a scenario where only 2 sheets are created from a total of 4):
=QUERY(
{IFERROR(INDIRECT("Sheet1!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet2!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet3!A1:B5"), {"",""});
IFERROR(INDIRECT("Sheet4!A1:B5"), {"",""})},
"where Col1 is not null", 0)
so, even if sheet names are predictable (which not always are) to pre-program 100+ sheets like this would be painful (even if there are various sneaky ways how to write such formula under 30 seconds)
an alternative would be to use a script to convert string and inject it as the formula
A1 would be formula that treates a string that looks like real formula:
=ARRAYFORMULA("=QUERY({"&TEXTJOIN("; ", 1,
IF(A3:A<>"", "'Week of "&LEFT(A3:A, 5)&"'!A1:D5", ))&
"}, ""where Col1 is not null"", 1)")
further populating of A6:A will expand the string automatically
then this script will take the string from A1 cell and it will paste it as valid formula into C5 cell:
function onEdit() {
var sheet = SpreadsheetApp.getActive().getSheetByName('Master Sheet');
var src = sheet.getRange("A1");
var str = src.getValue();
var cell = sheet.getRange("C5");
cell.setFormula(str);
}
of course, the script can be changed to onOpen trigger or with custom name triggered from the custom menu or via button (however it's not possible to use the custom function as formula directly)
If you're trying to update the data your query is looking at and you're feeding it a string, you need to put that string within the indirect() function. That will interpret your string as a data reference and point your query() in the right direction.
So for this you'd probably have
=QUERY(INDIRECT(I1),"Select *")

How to use a formula written as a string in another cell [evaluate for Google Spreadsheet] [duplicate]

This question already has answers here:
Is there a way to evaluate a formula that is stored in a cell?
(13 answers)
Closed last month.
I read several old posts about Google Spreadsheet missing the evaluate function.
There is any solution in 2016?
The easiest example.
'A1' contains the following string: UNIQUE(C1:C5)
'B1' I want to evaluate in it the unique formula written in 'A1'.
I've tried concatenating in this way: 'B1' containing ="="&A1 but the outcome is the string =UNIQUE(C1:C5).
I've also tried the indirect formula.
Any suggestion to break last hopes, please?
Additional note
The aim is to write formulas in a spreadsheet and use these formulas by several other spreadsheets. Therefore, any change has to be done in one place.
Short answer
Use a script that includes something like var formula = origin.getValue() to get the string and something like destination.setFormula(formula) to return the formula.
Explanation
As was already mentioned by the OP, Google Sheets doesn't have a EVALUATE() built-in function. A custom function can't be used because custom functions can only return one or multiple values but can't modify other cell properties.
A script triggered by a custom menu, events or from the Google Apps Script editor could be used to update the formulas of the specified cells.
Since the formulas will be kept as strings, it could be more easy to keep them in the script rather than in the spreadsheet itself.
Example
The following is a very simple script that adds the specified formula to the active range.
function addFormula() {
var formula = '=UNIQUE(C1:C5)';
var range = SpreadsheetApp.getActiveRange();
range.setFormula(formula);
}
I have a solution for my own use case. My investment broker exports data to its users in (badly-formatted) Excel. I do my own analysis in Google Sheets. I have found copy/pasting entire sheets of data to be accident-prone.
I have partially automated updating each tab of the records. In the sheet where I maintain all the records, the First tab is named "Summary"
Save the broker's .xlsx data to Google Sheets (File | Save as Google Sheets);
In the tab named Summary, enter into a cell, say "Summary!A1" the URL of this Google Sheet;
In cell A2 enter: =Char(34)&","&CHAR(34)&"Balances!A1:L5"&Char(34)&")"
In the next tab, enter in cell A1: ="IMPORTRANGE("&Char(34)&Summary!A1&Summary!A2
The leading double quote ensures that the entry is saved as a text string.
Select and copy this text string
in cell A3, type an initial "=" + Paste Special.
This will produce an importrange of the desired text, starting at cell A3

Google spreadhseet EVAL function

I have a google spreadsheet with different sheets, each one representing a different week.
For example:
1/12 - 1/16
1/19 - 1/23
I want to do a chart based on the content of those sheets. Is there any way I can make a formula and extract the name of the sheet from a content of a cell?
For example something like "=EVAL(A1)!$B$4", then I would have the content from "1/12 - 1/16"!$B$4 instead of having to go through each one of the weeks of the year manually.
Thanks for the help!
There’s no need to use AppScript, INDIRECT is enough to read a sheet name from a cell:
=INDIRECT(A1 & "!$B$4")
However, it looks like Andy’s answer is the way to go if you want to get the sheet name from its index rather than from a cell.
It'd be best to use AppScript. In Tools -> Script Editor make a new AppScript script:
function getSheetName(i) {
var s = SpreadsheetApp.getActiveSpreadsheet().getSheets()
return s[i].getName();
}
With that in your script, you can then use the custom function =getSheetName(<SHEETNUMBER>) and it will retrieve the sheet name based what sheet number it is (starting from 0). From there, just incorporate it into your formulas. You may need to use INDIRECT.
Example: =INDIRECT(getSheetName(1)&"!A1") to get cell A1 in the second sheet.

Possible to use Google Spreadsheet functions in Google App Script?

I just discovered Google App Scripts, and I'm stumped on something already...
I am trying to write a script for a Google Spreadsheet which finds certain historical stock prices. I found that the FinanceApp service within Google App Scripts has been deprecated, and seemingly replaced by the GOOGLEFINANCE() function within Google Spreadsheets. However, it returns an array, when I need only a single cell, and the array is mucking up the works.
So I'd like to write a short script that calls the GOOGLEFINANCE() spreadsheet function, and finds just the 1 piece of info I need from the array which is returned by GOOGLEFINANCE(). However, I cannot find a way to access Spreadsheet Functions (SUM, VLOOKUP, GOOGLEFINANCE, etc) within a script.
Is there a way to access these functions in a script? Or perhaps, is there a new service which replaces the deprecated FinanceApp service?
Many thanks for any assistance!
You can try this:
var trick = SpreadsheetApp.getActiveSheet().getRange('D2').setValue('=GOOGLEFINANCE("GOOG")').getValue();
Native Spreadsheet functions are not supported in Google Apps Script.
You could eventually use a somewhat cumbersome workaround by reading the value of a cell in which you write a formula (using script in both write and read) but this will be less than practical and / or fast.
You might try the INDEX function combined with GOOGLEFINANCE-
For reference,
=GOOGLEFINANCE("MSFT", "PRICE", "01/01/21")
Returns the array:
Date Close
1/4/2021 217.69
One can add the INDEX function to pick out specific elements from the array using the row,column coordinates of the array.
=INDEX(GOOGLEFINANCE("MSFT", "PRICE", "01/01/21"),2,2)
This returns just the data in row 2, column 2 - 217.69
There is one possible way, with the .setFormula(). This function behave like .setValue() and can be used the following way:
var ss = SpreadsheetApp.getActiveSpreadsheet();
var mySheet = ss.getSheets()[0]
//Code Below selects the first cell in range column A and B
var thisCell = mySheet.getRange('A:B').getCell(1,1);
thisCell.setFormula('=SUM(A2:A4)');
All formulas you write in this function are treated as strings must have ' or " within the .setFormula() input.

Use existing spreadsheet formulas in a custom formula in google docs/spreadsheets

I like writing my own formulas inside of Google Docs Spreadsheets. But often what I want to do is very similar to a function that already exists. As an example, I couldn't find a function to turn a date (31-Aug-2010) into the lexical day of the week (Tuesday). I'd like to write:
=LexWeekDay('31-Aug-2010')
'Tuesday'
Clearly I can write all of this logic using core javascript, but there already exists a normal spreadsheet function called WEEKDAY() which takes a date and converts into into a number representing the day of the week [0 => Sunday, 1=> Monday, etc].
How can I access this function (or generally any function), that speadsheets already define, from my custom script?
I asked the same question at Google help, but did not get a solution. According to user Ahab:
I understand the need. I voiced the
same in the GAS help forum 1 very
early one when GAS became available
but the reaction from the GAS team was
not very promising... :( In essence
we'd need a GAS class that contains
the spreadsheet functions do allow
using them.
Note that in general spreadsheet
functions already virtually can be
used as a functional programming
language without the need of scripting
them because of high-level functions
like ArrayFormula, FILTER, SORT,
UNIQUE, etc.. Unfortunately it is not
possible to create e.g. substitution
macro's that would allow us to quickly
re-use formulas like (in pseudo-macro
format):
Name: INVERSE Description: Inverse a
columnar array Syntax: #INVERSE( array
) Call: #INVERSE( #1 ) Execute:
=ARRAYFORMULA(SORT( #1 ; ROW( #1 ); FALSE))
In your custom appscript, you can use the in-built formulas of google spreadsheet in this way:
Lets say you want to use =WEEKDAY() function on cell A1.
Then, get your active spreadsheet like this in your custom appscript function:
var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("YOUR_SHEET_NAME");
now, set the formula like this:
sheet.getRange("A1").setValue("=WEEKDAY()");
Also, if you want to convert 0,1 etc to Sunday,Monday...then define an array like this:
var days = ['Sunday','Monday','Tuesday','Wednesday','Thursday','Friday','Saturday'];
And then use:
var dayIndex = sheet.getRange("A1").getValue();
Logger.log(days[dayIndex]);
You can see the logs using ctrl+enter or by going to View->Logs in script editor itself.