In a Google Docs spreadsheet, I'm looking for something like =EVAL(A1) where A1 is set to "=1+2".
I found out that in MS Excel there is an EVALUATE() function (which seems a bit tricky to use properly). But I could not find anything similar in Google Docs.
I also searched through the function list, but could not find anything helpful...
No, there's no equivalent to Excel's EVALUATE() in Google Sheets.
There's long history behind this one, see this old post for instance.
If you're just interested in simple math (as shown in your question), that can be done easily with a custom function.
function doMath( formula ) {
// Strip leading "=" if there
if (formula.charAt(0) === '=') formula = formula.substring(1);
return eval(formula)
}
For example, with your A1, put =doMath(A1) in another cell, and it will be 3.
I know this an old post. I'm just wondering, why nobody suggested:
myCell.getValue();
This will give you the result of the formula in myCell (3 in your example).
If you want to write the result to the cell (instead of the formula), you could use:
function fixFormula(myCell) {
myCell.setValue(myCell.getValue());
}
Short answer
As was mentioned previously, Google Sheets doesn't have a built-in EVALUATE function, but Google Sheets could be extended to add this function. Fortunately some SocialCalc files could be used to make this easier.
Script
On Google spreadsheet I'm sharing my progress. At this time I added the SocialCalc files that I think that are required and a couple of functions, and several test cases.
NOTES:
Google Sheets specific functions like FILTER, UNIQUE, among others are not available in SocialCalc as well as other functions like SIGN.
I think that the SocialCalc file should be replaced by those on https://github.com/marcelklehr/socialcalc as it looks to be updated recently. H/T to eddyparkinson (see https://stackoverflow.com/a/16329364/1595451)
Uses
The EVALUATE function on the linked file could be used as a custom function.
Example 1
A1: '=1+2 (please note the use of an apostrophe to make the formula be treated by Google Sheets as a string.
B1 formula:
=EVALUATE(A1)
B1 display value:
3
Example 2
To "EVALUATE" a formula like =VLOOKUP(2,A1:B3,2), at this time we need to use the "advanced" parameters. See the following example:
B1: '=VLOOKUP(2,A1:B3,2)
C1 formula:
=EVALUATE(B1,"data","A1:B3")
C1 display value:
B
Code.gs
/**
*
* Evaluates a string formula
*
* #param {"=1+1"} formula Formula string
* #param {"Tests"} sheetName Target sheet.
* #param {"A1"} coord Target cell.
*
* #customfunction
*
*/
function EVALUATE(formula,sheetName,coord){
// SocialCalc Sheet object
var scSheet = new SocialCalc.Sheet();
if(sheetName && coord){
// Pass values from a Google sheet to a SocialCalc sheet
GS_TO_SC(scSheet,coord,sheetName);
}
var parseinfo = SocialCalc.Formula.ParseFormulaIntoTokens(formula.substring(1));
var value = SocialCalc.Formula.evaluate_parsed_formula(parseinfo,scSheet,1); // parse formula, allowing range return
if(value.type != 'e'){
return value.value;
} else {
return value.error;
}
}
/**
*
* Pass the Google spreadsheet values of the specified range
* to a SocialCalc sheet
*
* See Cell Class on socialcalc-3 for details
*
*/
function GS_TO_SC(scSheet,coord,sheetName){
var ss = SpreadsheetApp.getActiveSpreadsheet();
if(sheetName){
var sheet = ss.getSheetByName(sheetName);
var range = sheet.getRange(coord);
} else {
var range = ss.getRange(coord);
}
var rows = range.getNumRows();
var columns = range.getNumColumns();
var cell,A1Notation,dtype,value,vtype;
// Double loop to pass cells in range to SocialCalc sheet
for(var row = 1; row <= rows; row++){
for(var column = 1; column <= columns; column++){
cell = range.getCell(row,column);
A1Notation = cell.getA1Notation();
value = cell.getValue();
if(cell.isBlank()){
dtype = 'b';
vtype = 'b';
} else {
switch(typeof value){
case 'string':
dtype = 't';
vtype = 't';
break;
case 'date':
case 'number':
dtype = 'v'
vtype = 'n';
break;
}
}
scSheet.cells[A1Notation] = {
datavalue: value,
datatype: dtype,
valuetype: vtype
}
}
}
}
formula1.gs
https://github.com/DanBricklin/socialcalc/blob/master/formula1.js
socialcalcconstants.gs
https://github.com/DanBricklin/socialcalc/blob/master/socialcalcconstants.js
socialcalc-3.gs
https://github.com/DanBricklin/socialcalc/blob/master/socialcalc-3.js
If you want to evaluate simple math(like A1: "(1+2)*9/3"), you can use query:
=query(,"Select "&A1&" label "&A1&" ''",0)
Basic math sent to query's select is evaluated by query.
Copy and paste the formulas:
Maybe you can copy and paste the formulas you need from "jQuery.sheet". Moved to:
https://github.com/Spreadsheets/WickedGrid
Looks to be all "open source"
Wont fix the issue
Also: The issue "Enable scripts to use standard spreadsheet functions" is marked as "Wont fix", see https://code.google.com/p/google-apps-script-issues/issues/detail?id=26
Ethercalc
there is a google like opensource spreadsheet called Ethercalc
GUI Code:
https://github.com/audreyt/ethercalc
Formulas: https://github.com/marcelklehr/socialcalc
Demo - on sandstorm:
https://apps.sandstorm.io/app/a0n6hwm32zjsrzes8gnjg734dh6jwt7x83xdgytspe761pe2asw0
In the case of evaluating a function like
"=GoogleFinance("usdeur","price",date(2013,12,1),date(2013,12,16))"
This can be done this without evaluate by directly referring to other cells like this:
=GoogleFinance(A10,"price",E3,E6)
Simple hack to evaluate formulas in google spreadsheet:
select cells or columns with formulas
go Edit -> Find and replace...
check "Also search in formulas"
replace "=" to "=="
replace back "==" to "="
in the same "Find and replace" window uncheck "Also search in formulas"
formulas will evaluate! :)
Thank you for user3626588's workaround here and it does indeed work. Based off your instructions it looks like it can be simplified even further.
In Cell B1 Enter the following:="=sum(A1:A5)"
In Cell C1 Set a data validation and select B1 with dropdown option.
Now select C1 and select the formula from the dropdown, it will sum any values between A1 through A5 automatically.
I have a sheet where I was creating a complicated formula for multiple values and this process worked!
Thank you once again as I was trying to avoid a script since I have data that is being pulled by another program on my worksheet. Script function do not always run automatically in those situations.
Here is the trick. Insert formula in the required cell, then get retrieve that cell value and replace the already inserted formula with this new value.
function calculateFormula(row, col){
var spreadsheet = SpreadsheetApp.getActive();
var sheet = spreadsheet.getSheetByName("Sheet Name");
sheet.getRange(row,col).setValue("=sum(D6,C12:C14)");
sheetData = sheet.getDataRange().getValues();
var newValue = sheetData[row-1][col-1];
sheet.getRange(row,col).setValue(newValue);
}
How about just converting a column of expressions which are not preceded by a "+"?
92/120
67/85
etc.
It's a bit of a hack, but this works
get the formula from the cell;
set the formula back again; then
get the value from the cell.
var cell = sheet.getRange("A1");
var formula = cell.getFormula();
cell.setFormula(formula);
var fileCell = cell.getValue();
Awesome work around for google not having evaluate(). I have looked all around and besides script have found no other way to have a formula as a string on one sheet then use that formula on another. In fact everything I've seen says you can't. Would be helpfull if anyone reading this could repost around if they come to an appropriate question since I must have read a half dozen posts saying it wasn't possible before I just rolled up my sleaves and done done it. :) It still has a little clunkyness since you need two cells in the spreadsheet you want the formula to execute, but here goes.
Ok, some set up. We'll call the spreadsheet with the formula as string SpreadsheetA, call the tab the formula is on TabAA, the Spreadsheet you want to call and execute said formula SpreadsheetB. I'll use a multi-tab example, so say you want the sum of A1:A5 on SpreadsheetB tab: TabBA to be calculated on SpreadsheetB tab: TabBB cell A1. Also call the URL of spreadsheet A: URLA
So, in Spreadsheet A Tab: TabAA cell A1 put ="=sum(TabBB!A1:A5)", therefore the cell will display: =sum(A1:A5). Note: you don't need any $ in formula. Then in Spreadsheet B, Tab: TabBB, cell A2 put: =Query(Importrange("URLA","TabAA!A1"),"select Col1 where Col1 <> ''"). That cell will now display =sum(TabBA!A1:A5). Next to that, cell A1 of Spreadsheet B tab: TabBB, create a dropdown of the cell with the formula in B2 (right click cell A1, select data validation, for Criteria select: List from range, enter B2 in box to right). That cell should now be summing SpreadsheetB, TabBA, range A1:A5
Hope that was clear, I'm rather novice at this. Also important, obviously you would only do this in cases where you wanted to choose from multiple formulas on spreadsheetA, instead of TabAA!A1 say you had another formula in A2 also so your query would be =Query(Importrange("URLA","TabAA!A1:A2"). I understand in the simplistic case given you would simply put the formula where you needed the sum.
Edit: Something I noticed, was when I wanted to use a formula with double quotes the above scenario didn't work because when you wrapped the formula with double quotes in double quotes you get an error since you need single quotes inside double quotes. The example I was trying: if(counta(iferror(query(B15:C,"select C where C = 'Rapid Shot' and B = true")))>0,Core!$C$18+$C$10&" / ",)&Core!$C$18+$C$10&if(Core!$C$18>5," / "&Core!$C$18-5+$C$10,)&if(Core!$C$18>10," / "&Core!$C$18-10+$C$10,)&if(Core!$C$18>15," / "&Core!$C$18-15+$C$10,)
In that case I put another formula into Spreadsheet A TabAA cell A2 that read ="="&A1. Then, ajusted the importrange referance in spreadsheet B to reference that cell instead.
BTW, this absolutly works so if you can't get it let me know where your having problems, I don't do a lot of colaboration so maybe I'm not saying something clear or using the right / best terminollagy but again I've seen many posts saying this was impossible and no one saying they had found another way.
Thanx ~ K to the D zizzle.
Here is the working trick to evaluate the concatenated formula string. Use the formula cell as a data validation source for the target cell. Maybe it is not a fully automated solution. But evaluating refreshed formulas has been stripped down to just one click. You just need to reselect the value from the validation box when it is necessary. Many thanks to #Aurielle Perlmann and #user3626588 for the idea.
As an example, when you have set up dynamic multiple concatenations of such below formula in another sheet, this will work well with selecting validation option.
In my case, pressing enter twice is not userfriendly.
=({FILTER(IMPORTRANGE("https://docs.google.com/spreadsheets/d/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/edit"; "EXPENSES!A2:P"); INDEX(IMPORTRANGE("https://docs.google.com/spreadsheets/d/aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa/edit"; "EXPENSES!A2:P"); 0; 1) <> ""); FILTER(IMPORTRANGE("https://docs.google.com/spreadsheets/d/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/edit"; "EXPENSES!A2:P"); INDEX(IMPORTRANGE("https://docs.google.com/spreadsheets/d/bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb/edit"; "EXPENSES!A2:P"); 0; 1) <> ""); FILTER(IMPORTRANGE("https://docs.google.com/spreadsheets/d/cccccccccccccccccccccccccccccccccccccccccc/edit"; "EXPENSES!A2:P"); INDEX(IMPORTRANGE("https://docs.google.com/spreadsheets/d/cccccccccccccccccccccccccccccccccccccccccc/edit"; "EXPENSES!A2:P"); 0; 1) <> "")})
[enter image description here]
[enter image description here]
I'm working on a spreadsheet which without macros would require a couple of million cells containing formulas, I'd like a macro which would take the formula in the top row of a selection, and then copy/autofill formula in to rows further down the sheet, and then replace those rows with values once the formulas have calculated.
As there are about 20,000 rows with ~20 columns, and several sheets which are like this, even if I ran a macro which would copy the formulas down through every sheet and then replace them with values, it would still be very laggy, so I'd like it to only copy the formulas in to rows where a cell in a specific column isn't empty (there is an input cell in each row), as well as only copying to rows where the cells to be filled are empty.
If we take the example of a simple setup of column 1 being for input, and column 2 being a formula based on it, and I had already run this after entering inputs up to the second row, and then entered stuff into the 3rd and 4th row too, with something that looked like this. I'd want the macro when run to copy the formulas in to just row 3 and 4 cells, then replace them with values.
I'm also uncertain how I could get formulas in non adjacent columns to be copied down through the same rows, as they can't be shown in one range.
Right now I have 2 basic macros, one which would copy formulas down through a set range, and one which would replace it with values, as when I had them in the same macro it seemed that the replacement would occur before the formulas had loaded, so it would be replaced with nothing.
If what I'm aiming for isn't possible with google sheets macros, but is possible with VBA in excel, I'm not ruling out the possibility I'll have to try and switch to excel, but would much prefer doing it in google sheets.
Thanks in advance for any help.
Edit: This is the very basic setup I have right now:
function Autofillformulas() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A4:C5000').activate();
spreadsheet.getRange('A4:C4').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
};
function Autofillvalues2() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A5:C5000').activate();
spreadsheet.getRange('A5:C5000').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
Edit 2: Thanks to comments I've now got it in to one macro:
function Autofillformulas() {
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A4:C5000').activate();
spreadsheet.getRange('A4:C4').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_NORMAL, false);
SpreadsheetApp.flush()
var spreadsheet = SpreadsheetApp.getActive();
spreadsheet.getRange('A5:C5000').activate();
spreadsheet.getRange('A5:C5000').copyTo(spreadsheet.getActiveRange(), SpreadsheetApp.CopyPasteType.PASTE_VALUES, false);
};
I currently have two sheets with user data, one is from active directory, the other is from a web based export.
My macro is supposed to place a vlookup function into B2:B24 with the column return going up by one
EG:
Dim beginPosition As Integer
For beginPosition = 1 To 24
Range(& beginPosition, "2").Select
=VLOOKUP($A2,Sheet2!$A$1:$X$5000,& beginPosition &,FALSE)
Next begin Position
Maybe I'm overthinking this and could use somethingn other than Vlookup to grab all the data from my web export sheet, but as of right now i'm stumped.
Thanks in advance
You can achieve this without looping
Range("B1:B24").Formula = "=VLOOKUP($A2, Sheet2!$A$1:$X$5000, ROW(),FALSE)"
I have a workbook with a number of cells that have data validation specified as a dropdown list of allowed values. Using EPPlus, I want to be able to get for each such cell, the list of allowed values.
So far I've got:
ExcelWorkSheet.DataValidations gives me an ExcelDataValidationCollection, which is a collection of IExcelDataValidation items for the worksheet.
Each IExcelDataValidation has an Address property of type ExcelAddress which presumably references all cells that have that validation rule.
The step I'm stuck on is finding if a given cell is one of the cells included in the ExcelAddress
Any takers?
I'm currently using EPPlus 3.1.1.0, but can upgrade to a more recent version if necessary.
UPDATE
I didn't explain this clearly enough. Here's my situation in more detail.
Assume column C has some cells with list data validation. Some cells allow, say, "A, B, C"; other cells allow "D, E, F" etc. The range of cells for each data validation list is not contiguous, so, for example:
C2, C4, C7-C10, C20 may allow "A,B,C"
C3, C5-C6", C15 may allow "D,E,F"
I'm trying to determine which cells allow "A,B,C" and which allow "D,E,F" etc.
ExcelWorksheet.DataValidations contains ExcelDataValidationList items, one with values "A,B,C", one with values "D,E,F", etc.
ExcelDataValidationList.Address for the list "A,B,C" contains an ExcelAddress whose Address property looks something like: "C4 C7:C10 C2 C20 ...".
I want to determine if a given cell (say C6) is included in the range specified by this address "C4 C7:C10 C2 C20 ...".
Of course, I can String.Split on whitespace, and parse each item in the resulting list. But I was hoping there would be some more direct way of doing this, e.g.
ExcelAddress.Contains("C6")
or
ExcelAddress.Contains(6, 2) // row 6 col 2 = C6
Almost there, just check the IExcelDataValidation's specific type. Tested with EPPlus 4.1.0.0:
using (var package = new ExcelPackage(new FileInfo(path)))
{
var sheet = package.Workbook.Worksheets[1];
var validations = sheet.DataValidations;
foreach (var validation in validations)
{
var list = validation as ExcelDataValidationList;
if (list != null)
{
var range = sheet.Cells[list.Formula.ExcelFormula];
var rowStart = range.Start.Row;
var rowEnd = range.End.Row;
// allowed values probably only in one column....
var colStart = range.Start.Column;
var colEnd = range.End.Column;
for (int row = rowStart; row <= rowEnd; ++row)
{
for (int col = colStart; col <= colEnd; col++)
{
Console.WriteLine(sheet.Cells[row, col].Value);
}
}
}
}
}
Test worksheet:
Output:
one
two
three
Has anyone come across a situation where Excel seems to manipulate your formulas.
I have a sheet where I have an Index value in Column A. The First row starts with any non zero Value. Subsequent rows in the column increment the value. Eg
A1 = 1000
A2= A1+ 1
A3= A2 + 1
and so on/
I have another column B whose values will either be blank or a formula pointing to column A(usually the subsequent rows)
Eg:
B1.Formula = "=A2"
B2.Formula = "=A3"
B3.Value = ""
B4.value = "=A6"
Now I have a backup-restore functionality that lets me write out the data/formulas to a text file and then read it back in another workbook.
In the case of columns A and B, I am checking if the text value starts with "=" and then set either the value or formula of that cell depending on whether there is a formula or not.
So far the functionality has worked fine. It lets me restore accurately.
Now, if I convert this data range to a table and modify the code accordingly the behaviour is strange. I am using the ListObject structure to refer to the table. So for Column B my restore code is:
If Left(soureString) = "=" Then
'This is a formula
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Formula = sourcestring
Else
'This is a value
Sheets("MySheet").ListObjects(1).ListColumns("Next").DataBodyRange(row).Value = soureString
End If
once I am done writing a row, I loop to the start and
Dim newRow AS listrow
Set newRow = Sheets("MySheet").Listrows.Add(AlwaysInsert:=False)
row = newRow.Index
But this time when I run the process. this is what I get:
B1.Formula = "=A5"
B2.Formula = "=A5"
B3.Value = ""
B4.value = "=A5"
Why are my formula values all changing to the same value when I use a table instead of a range?
I had the same issue when populating a ListObject (Table) from an Excel Add-in, setting AutoFillFormulasInLists was the solution.
My workaround is to save the current setting, set AutoFillFormulasInLists to false, populate the table with data, formulas etc, then set AutoFillFormulasInLists back to the original setting.
bool OriginalAutoFillFormulaInListsFlag = app.AutoCorrect.AutoFillFormulasInLists;
app.AutoCorrect.AutoFillFormulasInLists = false;
//[ListObject population code....]
if (OriginalAutoFillFormulaInListsFlag == true)
{
app.AutoCorrect.AutoFillFormulasInLists = true;
}
Hope this helps someone.
I faced a similar issue. Ideally you could tell excel to stop doing this but I haven't been able to figure out how. Supposedly doing the following is supposed to keep excel from copying the formulas:
xlApp.AutoCorrect.AutoFillFormulasInLists = false
but it didn't work for me.
Using the answer from this question How to create running total using Excel table structured references? helped me. It doesn't feel like the ideal solution but it does do the job.
I used this formula where Weight is a column name from my table. #This Row is a "Special item specifier" and has a special meaning. The syntax looks a little funky because it's what's called a Structured Reference:
=AVERAGE(INDEX([Weight],1):[[#This Row],[Weight]])
The INDEX([Weight],1) part gives the reference for the 1st row in the Weight column
While the [[#This Row],[Weight]] part gives the reference for the current row in the Weight column.
So for example, if Weight is column J, and the current row is, say, 7 then this is equivalent to
=AVERAGE(J1:J7)
and on the 8th row it will be equivalent to
=AVERAGE(J1:J8) and so on
I have found that the only way to solve the problem of formulas changing in Excel Tables when you insert in VBA is to insert at the first row of the table, NOT the bottom or the middle. You can sort after.
And I always select or reference the EntireRow to do my insert in the Worksheet object not in the table itself. I always put a table in its own Worksheet anyway using xx.Entirerow.Insert.