Formula Array returns nothing or just #VALUE! when using Evaluate - vba

Im trying to evaluate Formula array to find a value from sheet with multiple criteria. The sheet name is Holiday_master
So in another sheet Temp_data I tried to exceute the following code to set that value in that cell using formula array
Public Function getCostMultiplier(dt As Date, wt As String) As Double 'dt is the date of entry, wt working time name "India Full Time","Singapore f........
Dim lRow As Integer
Dim we As Boolean
we = IsWeekend(dt)
Dim nhRange As Range
Dim d As Double
d = CDbl(dt)
Dim location As String
Select Case LCase(Trim(wt))
Case "india full time 45"
location = "INDIA"
Case "singapore full time 40"
location = "SINGAPORE"
Case "uk full time 40"
location = "UK"
End Select
Dim n As Integer
'n = Application.Evaluate()
lRow = Sheets("Holiday_master").Range("A1000").End(xlUp).Row + 1
Dim formula As String
Dim s As String
s = Application.Evaluate("RC11")
formula = printf("{=INDEX( Holiday_master!R2C3:R{0}C3,MATCH(1,(""{1}""=Holiday_master!R2C2:R{0}C2)*({2}=Holiday_master!R2C1:R{0}C1),0),1)}", lRow, location, d)
''''INDEX( Holiday_master!R2C3:R11C3,MATCH(1,("INDIA"=Holiday_master!R2C2:R11C2)*(43126=Holiday_master!R2C1:R11C1),0),1)
n = Application.Evaluate(formula)
getCostMultiplier = n
End Function
Public Function printf(mask As String, ParamArray tokens()) As String
Dim i As Long
For i = 0 To UBound(tokens)
mask = Replace$(mask, "{" & i & "}", tokens(i))
Next
printf = mask
End Function
So in Temp_data sheet in a cell I set the formula as getCostMultiplier(RC11,RC4)', so obviously it reaches my function with parameters26-01-2018andINDIA`
So in the code we have a final formula which is I commented there INDEX( Holiday_master!R2C3:R11C3,MATCH(1,("INDIA"=Holiday_master!R2C2:R11C2)*(43101=Holiday_master!R2C1:R11C1),0),1)
But its not evaluating as I expected or this could not be the way for evaluation of formula array.
If I execute that formula in that cell manually and on submitting (ctrl+shift+enter) it executes properly and returning the value.
So I didnt understand how to do that same from VBA or how to do that Evaluation. I never used Evaluation before

Convert the formula from xlR1C1 to xlA1 syntax.
n = Application.Evaluate(Application.ConvertFormula(formula, xlR1C1, xlA1))
Using Evaluate within VBA should be done with xlA1 syntax.

I got it working with R1C1 itself. The issue was, I specified curly braces there in the formula array which is not required in the case of Evaluate
So here is my modified formula code with error validation also added
formula = printf("=IFERROR(INDEX(Holiday_master!R2C3:R{0}C3,MATCH(1,(""{1}""=Holiday_master!R2C2:R{0}C2)*({2}=Holiday_master!R2C1:R{0}C1),0),1),1)", lRow, location, CStr(d))

Related

LibreOffice Calc: Can I get the cell address from VLOOKUP?

I'm using VLOOKUP, in Calc, like this:
VLOOKUP(B11,G2:J7,4,0)
Normally when any of us uses this, we want to get the value in the cell this function finds. In this case, rather than the value, I'd like to get a string with the cell address in it instead or the row and column of that cell. For instance, if I have a double precision floating point value of 30.14 in cell J5 and that's the answer, rather than having it return 30.14, I want it to return something like "J5" or 9,4 or some other way for me to read the result in a macro.
I've tried using =ADDRESS() and =CELL("address", ) but I'm getting errors (=CELL() gives me '#REF!').
EDIT: I'm using this routine as a wrapper around VLOOKUP with a table of floating point numbers (which is why it returns a DOUBLE instead of getting the cell value as a STRING or something else). All I have to do is pass it the column I want to get the data from:
Function getLookup(valColumn as Integer) as Double
oDoc = ThisComponent
oSheet = oDoc.Sheets (workSheet)
rangeInfo = lookupTopLeft + ":" + lookupBottomRight
cellRange = oSheet.getCellRangeByName(rangeInfo)
oCell = oSheet.GetCellByPosition(dataCellColumn, dataCellRow)
searchValue = oCell.getString()
Mode = 0
svc = createUnoService( "com.sun.star.sheet.FunctionAccess" )
args = Array(searchValue, cellRange, valColumn, Mode)
getLookup = svc.callFunction("VLOOKUP", args)
End Function
Note I'm using some local variables in this. They're private, for the module only, so I don't have to change cell references in multiple places while I'm working on designing my spreadsheet. "lookupTopLeft" and "lookupBottomRight" are "G2" and "J7", the top left and bottom right cells for the data I'm working with. "dataCellColumn", and "dataCellRow" are the column and row coordinates for the source for the key I'm using in VLOOKUP.
(#JohnSUN, I think this may be modified from an answer you provided somewhere.)
I'd like to be able to do a similar wrapper routine that would return the column and row of a cell instead of the value in the cell.
One of many possible options:
Option Explicit
Const lookupTopLeft = "G2"
Const lookupBottomRight = "J7"
Const dataCellColumn = 1
Const dataCellRow = 10
Const workSheet = 0
Function getCellByLookup(valColumn As Integer) As Variant
Dim oSheet As Variant, cellRange As Variant, oCell As Variant
Dim oColumnToSearch As Variant
Dim oSearchDescriptor As Variant
Dim searchValue As String
Dim nRow As Long
oSheet = ThisComponent.getSheets().getByIndex(workSheet)
cellRange = oSheet.getCellRangeByName(lookupTopLeft + ":" + lookupBottomRight)
searchValue = oSheet.GetCellByPosition(dataCellColumn, dataCellRow).getString()
Rem If we are looking not for a value, but for a cell,
Rem then using VLOOKUP is unnecessary, a simple Find is enough
oColumnToSearch = cellRange.getCellRangeByPosition(0, 0, 0, _
cellRange.getRows().getCount()-1) ' Resize full range to one first column
Rem Set search params
oSearchDescriptor = oColumnToSearch.createSearchDescriptor()
oSearchDescriptor.setSearchString(searchValue)
oSearchDescriptor.SearchType = 1 ' Search in Values!
Rem Try to find searchValue in oColumnToSearch
oCell = oColumnToSearch.findFirst(oSearchDescriptor)
If Not IsNull(oCell) Then ' Only if the value was found
nRow = oCell.getRangeAddress().StartRow
Rem Offset oCell to valColumn
oCell = cellRange.getColumns().getByIndex(valColumn-1).GetCellByPosition(0,nRow)
getCellByLookup = Replace(oCell.AbsoluteName, "$", "")
Else ' If the value from B11 is not found - warn about it
getCellByLookup = "Not found"
EndIf
End Function

VBA to format PNEZD string in Excel

I have a large list of points with PNEZD (point, northing, easting, elevation, description) fallowed by a few attributes for each point. how can I combine these extra attribute columns to my D column separated by a space? And can i do that with a push of a button?
example of a few points would be
10,1000,5000,50,tree,birch,12IN
11,1100,5500,55,tree,spruce,10IN
12,1130,5560,54,powerpole,tele,wood,12IN,guyed
I want to combine the last few so it will read:
10,1000,5000,50,tree birch 12IN
11,1100,5500,55,tree spruce 10IN
12,1130,5560,54,powerpole tele wood 12IN guyed
please help!
You can create a user-defined function in VBA that you can then use on your worksheet as any normal formula.
From Excel, press Alt+F11 to get to the VBA editor. Once there, press Ctrl+R to show or activate the Project Explorer. Right-click on the Modules node, and select Insert -> Module. Put the code below into the module:
Option Explicit
Public Function GetPnezdLabel(pnezd As Variant) As Variant
On Error GoTo errHandler
Dim result As Variant
Dim parts As Variant
Dim lastPartIndex As Long
Dim intermediatePartIndex As Long
Dim index As Long
result = ""
parts = Split(CStr(pnezd), ",", Compare:=VbCompareMethod.vbBinaryCompare)
lastPartIndex = UBound(parts)
intermediatePartIndex = Application.WorksheetFunction.Min(3, lastPartIndex)
For index = 0 To intermediatePartIndex
result = result & parts(index) & IIf(index < lastPartIndex, ",", "")
Next
For index = 4 To lastPartIndex
result = result & parts(index) & IIf(index < lastPartIndex, " ", "")
Next
Done:
GetPnezdLabel = result
Exit Function
errHandler:
result = XlCVError.xlErrValue
Resume Done
End Function
Press Alt+F11 to return to Excel. Now, assuming you have a PNEZD value in cell A1, you can create a formula in e.g. B1 as follows:
=GetPnezdLabel(A1)
and you'll see the result appear.
The principle behind the GetPnezdLabel function is to split the parameter's value at commas and to build the result from the parts.
For information, the same result can be achieved through following formula.
Assuming your data is in cell A2 then in cell D2 insert following formula and copy down.
=TRIM(LEFT(SUBSTITUTE(A2,",",REPT(" ",199),4),199))
&","&
SUBSTITUTE(TRIM(RIGHT(SUBSTITUTE(A2,",",REPT(" ",199),4),199)),","," ")
As you've indicated VBA specifically, you may not want a formula solution.

Excel formula calculating once then deleting

I have an excel formula:
=SplitKey(GetSysCd(INDEX([ReportValue],MATCH("mtr_make_model",[FieldName],0)),INDEX([ListName],MATCH("mtr_make_model",[FieldName],0))), 0)
which is running a few subroutines in VBA, but mainly matching values and inserting those values into a cell. When it finds a value for "mtr_make_model" it runs and matches the values inside a sys codes table. The issue I am having is that it is calculating once and then it removes the formula and now has solely the value... In the event that I go to the mtr_make_model field and change the value, the formula does not recalculate. Has anyone heard of this happening? Is this due to something in the VBA code? How do I make that formula stay put and if certain values change, the formula recalculates?
Thanks in advance.
Here are the two functions:
Public Function GetSysCd(ByVal name As String, sysCdTableName As String) As String
Dim r As Integer
Dim sysCdTable As Range
Dim nameList As Variant
Dim sysCd As String
On Error GoTo GetSysCd_Error
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
nameList = WorksheetFunction.Index(sysCdTable, 0, 2)
r = WorksheetFunction.Match(name, nameList, 0)
sysCd = WorksheetFunction.Index(sysCdTable, r, 1)
GetOutOfHere:
On Error GoTo 0
GetSysCd = sysCd
Exit Function
GetSysCd_Error:
sysCd = ""
GoTo GetOutOfHere
End Function
Public Function SplitKey(s As String, v As Integer)
Dim aString As Variant
Dim r As Integer
If Len(s) > 2 Then
aString = Split(s, "_")
If v = 0 Or v = 1 Then
SplitKey = aString(v)
Else
SplitKey = aString(0)
End If
Else
SplitKey = ""
End If
End Function
I don't think the functions are relevant at this point, but rather just a matter of the function not recalculating when a variable in the formula changes...
The problem could be that Excel only recalculates functions when one of their arguments changes, and your GetSysCd function is referring to a range that is not in its argument list
Set sysCdTable = Worksheets("sys_cd").Range(sysCdTableName)
where sysCdTableName is just a string rather than a reference.
You can make the functions recalculate in real time by adding Application.Volatile True to the top of each function.

How to aggregate returns in an excel UDF using the product formula

I am trying to put the below formula into a UDF so that I can get a cumulative return when I aggregate monthly returns.
In excel the formula has to be recognized as an array so when I type it in I press Ctrl + Shift + Enter to get the {} brackets around the formula.
Does anyone know how to do this?
I want to be able to just type in returns_calc() and select the range that would fit into the returns variable below.
{=(PRODUCT(1+returns/100)-1)*100}
You can use the [ ] notation in Application.Evaluate to calculate Array Formulas in VBA. Your above formula can be called in VBA in just 1 line as shown below
Sub Sample()
MsgBox Application.Evaluate(["=(PRODUCT(1+returns/100)-1)*100"])
End Sub
Now modifying it to accept a range in a function, you may do this as well
Function returns_calc(rng As Range) As Variant
On Error GoTo Whoa
Dim frmulaStr As String
frmulaStr = "=(PRODUCT(1+(" & rng.Address & ")/100)-1)*100"
returns_calc = Application.Evaluate([frmulaStr])
Exit Function
Whoa:
returns_calc = "Please check formula string" 'or simply returns_calc = ""
End Function
EXAMPLE SCREENSHOT
Something like this
Public Function Range_Product(theRange As Variant)
Dim var As Variant
Dim j As Long
var = theRange.Value2
Range_Product = 1#
For j = LBound(var) To UBound(var)
Range_Product = Range_Product * (1 + var(j, 1) / 100)
Next j
Range_Product = (Range_Product - 1) * 100
End Function

How do I get the cell value from a formula in Excel using VBA?

I have a formula in a range of cells in a worksheet which evaluate to numerical values. How do I get the numerical values in VBA from a range passed into a function?
Let's say the first 10 rows of column A in a worksheet contain rand() and I am passing that as an argument to my function...
public Function X(data as Range) as double
for c in data.Cells
c.Value 'This is always Empty
c.Value2 'This is always Empty
c.Formula 'This contains RAND()
next
end Function
I call the function from a cell...
=X(a1:a10)
How do I get at the cell value, e.g. 0.62933645?
Excel 2003, VB6
The following code works for me when running from VBA (Excel 2003):
Public Function X(data As Range) As Double
For Each c In data.Cells
a = c.Value 'This works
b = c.Value2 'This works too (same value)
f = c.Formula 'This contains =RAND()
Next
End Function
a and b are the same and equal what I'm passing in (which is a range of cells with Rand() in them). I'm not sure what else is going on here.
Aha! You need to set X, no? I'm not sure what exactly you expect this function to do, but you need to set X (the name of the function) to the value you want returned. Add this line:
X = a
I can't replicate a problem using the layout you posted. I noticed a few syntax errors in your posted code (ie: "for" should be "for each"). But when I put =RAND() in A1:A10 and =X(A1:A10) I got a return just fine with this:
Public Function X(data As Range) As Double
Dim c As Excel.Range
Dim sum As Double
For Each c In data.Cells
sum = sum + c.Value
Next
X = sum
End Function
However, just to a expand a little more on a few of the other questions you brushed up against. You can evaluate a formula for a result like so:
Public Function X(data As Range) As Double
Dim c As Excel.Range
Dim sum As Double
For Each c In data.Cells
sum = sum + Excel.Evaluate(c.Formula)
Next
X = sum
End Function
But generally speaking you won't want to, as this is basically calculating the same value twice.
Make sure you do a calculate before requesting the value.
To Speed up macros something like the following is often preformed..
'Set Reasonable default
Application.CutCopyMode = False
Application.ScreenUpdating = False
Application.Interactive = False
Application.Calculation = xlCalculationManual
in this state you must force calculation before the value will be available.
Public Function X(data As Range) As Double
'You may need the following as well
'Application.Calculate
Dim c As Range
For Each c In data.Cells
c.Calculate
c.Value 'This is now has a value
c.Value2 'This is now has a value
c.Formula 'This contains RAND()
Next
End Function