Evaluate a string to a formula in Google Sheets - google-apps-script

I am writing a math paper where i would like to display my calculations (formulas) separately from the solution.
I am currently working in Google Sheets.
The end goal would be to have one column with formulas and one column with answers.
I tried to work with GS to write a function that would take the string value from A1 and evaluate it in the B1 column.
I used this simple script that i found on:
https://support.google.com/docs/thread/13826624/evaluate-string-as-formula?hl=en
function run(input){
return eval(input);
}
It works with simple calculations like division, multiplication, addition and subtraction.
But the script doesn't solve basic exponents like 1 * 10^3 (it gives me 9). And square roots like sqrt(9) (gives me an #error)
I'm not sure which way to go from here.

The easiest solution would probably be to work the other way around: write your formulas normally, and use the formulatext() spreadsheet function to display the formula in an adjacent cell.

Related

How to query an array in Google Sheets?

In Google Sheets i have in a cell an array like [27, https://www.example.com/page1.html, false, false, 1]
How can i access its single parts with a formula?
I know a way through =SPLIT(), like =split(split(A2,"["),",") - but i would very like, if its possible, to access each part directly (each array has always the same amount of parts in my data set).
Maybe something like =QUERY(query(A2,",",1)) - cell, divider, item number...? - Result is 27.
=INDEX(SPLIT(A2,"[,]"),1)
SPLIT by each of these characters [,]
INDEX into the resulting array
I would like to take the chance and propose a solution using Google Apps Script. Basically, you can create your own custom function to accomplish this task.
Please follow these steps:
Go to Tools => Script editor from your spreadsheet file:
Clear the default Code.gs file, copy and paste this function and save the changes:
function indexArray(arr,pos) {
return array=arr.slice(1,-1).split(",")[pos-1]
}
You are now able to access the indexArray() function from within your spreadsheet file. It accepts two arguments, the desired cell that contains the array and the position of the element you would like to access, starting from 1:
=indexArray(A2,2)
For example, this will give you the second element of your array which is: https://www.example.com/page1.html.
Check these instructions out if you need more information how custom functions work. They are pretty straightforward.

Is it possible to determine if a number is recursive in Google Sheets?

So I'd like 1/3 (which equals 0.33333 recurring) to return true and 1/8 (which equals 0.125 non-recurring) to be false.
Something like =ISRECURRING(A1) which returns a boolean.
The reason for this is that I want to highlight cells that have had rounding applied to them.
You can build a JavaScript function to check that and use it in your sheet as an Apps Script custom function. To achieve this, follow these steps:
In your spreadsheet, select Tools > Script editor to open a script bound to your file.
Copy this function in the script editor, and save the project (credits to Larry Battle, whose answer here this function is based on):
function ISRECURRING(num) {
num = (num || "").toString().replace(/\d$/, '');
var RE_PatternInRepeatDec = /(?:[^\.]+\.\d*)(\d{2,})+(?:\1)$/;
return RE_PatternInRepeatDec.exec(num) ? true : false;
};
Now, if you go back to your spreadsheet, you can use this function as if you were using a regular sheets formula. You just have to provide the appropriate range as an argument, as you can see here:
Note:
As #Ron Rosenfeld noted in a comment below, this function will not work for very long repetends (e.g. 1/97). This is due to the precision limit that spreadsheets have (15 digits, as far as I know). Because of this, repetends longer than 7 digits won't be detected.
Reference:
Custom Functions in Google Sheets
Credit to #Ron Rosenfeld.
=A1-ROUND(A1,14) <> 0 works a treat. Where A1 is the number in question.
I can't guarantee that every rounded number works but it appears to work on all the examples I've tried.

A function to format numbers as written in Germany

I am writing my first script for an invoice template based on a Google Doc.
It works fine. I need to use it in Germany so I need to be able to format the prices in the way they are shown in Germany.
I have written a short function to do this but am interested in whether there is a better or more obvious way of achieving this.
// this formats numbers as used in Germany
function numberToGerman(number){
number = Utilities.formatString("%d.%d,%02d", number/1000, number%1000, number%1*100);
return number;
}
It has a problem above 999.999,99 or below 1.000,00 at the moment but I hope there is a better solution out there.
Here is a slight modification of the answer by Elias Zamaria, replacing separators and ensuring exactly two decimals:
function numberToGerman(number){
var parts = number.toString().split(".");
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ".");
parts[1] = ((parts[1] || "") + "00").slice(0,2);
return parts.join(",");
}
The parts are the integer and fractional parts. Groups of three digits are processed by the regex, which places dots accordingly. The fractional parts is expanded/truncated to two places.
Remarks
Unfortunately, number.toLocaleString("de-DE") is not yet supported by Google Apps Script. One can use number.toLocaleString() but then the performance depends on someone having set the correct locale. Also, options concerning decimal places aren't supported either, so this doesn't help that much.
If this was to be used in a spreadsheet, you can apply appropriate formatting to cells either manually from the menu, or from a script: for example cell.setNumberFormat("#,###.00"). This looks like U.S.-style formatting, but once the locale of the spreadsheet is changed to Germany, it changes accordingly, e.g. 1.234.567,89.

Accessing the Cell Coordinates in custom function

Is there a way to access the cell coordinates (in A1 notation) of the cell that was passed in to my function?
For example, if my function is this
function displayA1Notation(myCell){
return myCell.getA1Notation();
}
and I put the following in cell B4:
=displayA1Notation(C6)
I'm hoping to see this:
C6
But what I actually see is this:
Kansas
("Kansas" is the actual cell value of C6)
I know this seems easy... I'm just stuck in trying to get it to work.
Thanks~!
Spreadsheet custom functions arguments contain only values pointed as arguments and not cell addresses. This fact not clear documented but there are a couple of similar questions here, for instance, this one. There is a workaround by using the build-in function ADDRESS. like in the following code
=myFunc(ADDRESS(ROW(F8), COLUMN(F8)))

OnEdit-trigger combined with cell-functions

I have an onEdit-script which calculates the value of a cell based on the content of a row of other cells.
I need this script to be in an onEdit-trigger rather than a regular cell-function because I don't always want the calculation to be redone when the value in one of the targeted cells is changed, but instead it checks for certain conditions and recalculates only when those are met.
A small problem I'm having with this is that one of the cells that the onEdit-script reads data from contains a function rather than a simple number.
This causes a problem because sometimes when I enter data in a cell, it will trigger both this cell-function and the onEdit-script. And most of the time the oEdit-function runs before the cell-function finishes so the onEdit-function just picks up "Thinking" from this cell and thus it returns NaN.
So I guess a convenient solution to this would have been to make the onEdit-function wait for the "targeted" cells to finish their calculation but I don't think there is a way to do this?
Of course I could move the cell-function(which basically is a SUM-function with some added functionality) to the onEdit-script, which would solve the issue.
But to me it doesn't seem so nice having all interactivity in the onEdit-trigger. Or am I just being silly?
Or is there another approach I could take somehow?
I think the best solution would be to replace your custom formula (that "thinks") with a regular spreadsheet formula if possible. And it seems to be your case, since it's just a SUM function with "added functionality". Probably a SUMIF will suffice.
If your function is indeed complicated and can't be written as regular formula (which I really doubt), the best solution would be indeed to move the calculation to the onEdit-trigger.
Regardless of your problem ,I always advise to not use custom formulas, they are really problematic. e.g. haven't you ran into the caching issue?
You might try:
SpreadsheetApp.flush();
at the beginning of your onEdit() script to force recalc before continuing.
http://code.google.com/googleapps/appsscript/class_spreadsheetapp.html#flush
To get onEdit to wait for the calculations, you might try:
function onEdit(e) {
// ...some if-condition that makes sure the following sleep is not called whenever any cell in your spreadsheet is edited
Utilities.sleep(1000); // 1000 is the amount of milliseconds
}
https://developers.google.com/apps-script/reference/utilities/utilities#sleep(Integer)
Although using sleep is brittle, in case the calculations take more or less time than you anticipated. It can introduce timing issues which are hard to debug.
So I guess a convenient solution to this would have been to make the onEdit-function wait for the "targeted" cells to finish their calculation but I don't think there is a way to do this?
To have the aforementioned if-condition reference targeted cells (a specific range), see these answers: Google Spreadsheet SCRIPT Check if edited cell is in a specific range