Evaluate function giving error 2029 using Excel VBA - excel

Evaluate is throwing error 2029 (#NAME).
The project is using Census procedures to allocate seats in the House of Representatives for any given number of house members (currently 435). There are a number of proposals to expand the size of the House. I'm investigating budget and economic implications.
My workbook has a sheet named PVC. I'm trying to find the maximum value in column E (number of seats allocated to a given state) for a value in column C (two-letter state abbreviations).
I have removed quite a few lines.
I have experimented with a user-defined function MaxIf().
Sub CountSeatsEval()
Dim lNoSeats, lG2, lastrow, lStateRow, lStateSeats, lStateNo As Long
Dim sFileName, sPathName, sFunction, sSearchValue, sSearchState As String
Dim sStateAbbr, vStateSeats As Variant
Dim wsSource, wsTarget As Worksheet
Dim rMaxRange, rSearchValue, rSearchState As Range
Dim rLookup1 As Range
Set wsSource = ThisWorkbook.Worksheets("PVC")
'Following line is to make life easy temporarily
Set wsTarget = ThisWorkbook.Worksheets("PVC")
lNoSeats = wsSource.Range("G2").Value
...
'Copy and paste G2 to replace formula with value
wsTarget.Range("G2").Copy
wsTarget.Range("G2").PasteSpecial (xlPasteValues)
lastrow = wsTarget.Cells(Rows.Count, 6).End(xlUp).Row
...
sSearchValue = "'PVC'!E2:$E$" & lastrow
sSearchState = "'PVC'!$C$6"
...
sStateAbbr = "CA"
lStateRow = 6
vStateSeats = Evaluate("IF((MAXIFS(sSearchValue, sSearchState, sSearchState))>0,(MAXIFS(sSearchValue, sSearchState, sSearchState)),1)")
End Sub

sSearchValue and sSearchState are VBA local variables:
Dim sFileName, sPathName, sFunction, sSearchValue, sSearchState As String
Note that this statement declares sSearchState as a String, and then leaves all other 4 variables without a declared type, making them implicit Variant variables (see VariableTypeNotDeclared Rubberduck inspection details).
Being local VBA variables, they live in the VBA runtime context, and Excel doesn't have the slightest idea about their existence - so you get a #NAME? error:
In Excel you get a #NAME? error whenever you try to evaluate a formula that contains a name that Excel cannot resolve in the current context.
So you need to have VBA evaluate the variables' values before you send the resulting expression over to Excel's calculation engine; you can do this by splitting up the string and using the concatenation operator (&):
vStateSeats = wsTarget.Evaluate("IF((MAXIFS(" & sSearchValue & "," & sSearchState & "," & sSearchState & "))>0,(MAXIFS(" & sSearchValue & "," & sSearchState & "," & sSearchState & ")),1)")
Unqualified, Evaluate will invoke [_Global].Evaluate, which is essentially Application.Evaluate, which may or may not produce the desired results - by qualifying it with a specific Worksheet object, the formula evaluates in the context of that worksheet.

Related

How to reference the far right worksheet in another workbook, for a VLOOKUP?

I am writing a vlookup function and I want to reference the last tab in a another workbook, rather than the tab name as it changes daily.
To move to the last tab in the workbook I have
Sheets(Sheets.Count).Select
Is it possible to reference this in the a VLOOKUP? (left a blank where the worksheet should be)
"=VLOOKUP(RC[133],'[Unavista UTI Lookup December2019.xlsm]" "'!C4:C7,4,0)"
This is just off the top of my head (untested), but maybe something like this
Dim wbData as Workbook
Set wbData = Workbooks.Add("Unavista UTI Lookup December2019.xlsm")
ActiveWorkbook.Worksheets("Sheet1").Range("A1").Formula = "=VLOOKUP(RC[133],[" & wbData.Name & "]" & wbData.WorkSheets(wbData.WorkSheets.Count).Name & "!C4:C7,4,0)"
wbData.Close False
Alternatively, using the .Open method:
Dim wbData as Workbook
Set wbData = Workbooks.Open(Filename:="Unavista UTI Lookup December2019.xlsm", ReadOnly:=True)
ActiveWorkbook.Worksheets("Sheet1").Range("A1").Formula = "=VLOOKUP(RC[133],[" & wbData.Name & "]" & wbData.WorkSheets(wbData.WorkSheets.Count).Name & "!C4:C7,4,0)"
wbData.Close False
Adjust Sheet1 and A1 to your needs
But still, you would need to trigger this code on an event of some sort, which isn't clear to me yet.
The Function I am at is below;
Currently i am changing the date of the worksheet which the vlookup is referencing. So the below shows "Dec 9" from yesterday. Once the new tab gets created today in the file "Unavista UTI Lookup December2019" - It will show "Dec 10"
ActiveCell.FormulaR1C1 = _
"=VLOOKUP(RC[133],'[Unavista UTI Lookup December2019.xlsm]Dec 9'!C4:C7,4,0)"
What I am trying to do is find a way for the code to automatically change this to todays date. Speaking with someone at work we tried the below
Dim DayValue As String
Dim FormulaString As String
DayValue = Format(Date, "mmm d")
Range("E2").Select
FormulaString = "=VLOOKUP(EH2,'[Unavista UTI Lookup December2019.xlsm]" & DayValue & "' !$D:$G,4,0)"
The formula isn't bringing back any error, but unfortunately the lookup isn't doing anything, and just bringing back empty cells.
If anyone can resolve this, as i've tried numerous things and still not been able to get it to work.
I would assume that your were applying the formula with a line like this:
Range("E2").Formula = FormulaString
however when I tried that line, it returned an error as your line has one blank space in excess, as the syntax for a link with another workbook should be:
'[WorkbookName]WorksheetName'!Range
and your line has:
'[WorkbookName]WorksheetName' !Range
Therefore instead of this:
FormulaString = "=VLOOKUP(EH2,'[Unavista UTI Lookup December2019.xlsm]" & DayValue & "' !$D:$G,4,0)"
use this:
FormulaString = "=VLOOKUP(EH2,'[Unavista UTI Lookup December2019.xlsm]" & DayValue & "'!$D:$G,4,0)"
Range("E2").Formula = FormulaString
However, in order to ease the maintenance of the procedure I suggest to use a constant, try this procedure:
Sub Formula_Today()
Const kFml As String = "= VLOOKUP( RC[133]," & _
"'[Unavista UTI Lookup December2019.xlsm]#TODAY'!C4:C7, 4, 0 )"
Dim sFml As String
sFml = Replace(kFml, "#TODAY", Format(Date, "mmm d"))
With ThisWorkbook.Worksheets("DATA")
.Range("E2:E10").FormulaR1C1 = sFml
End With
End Sub
Before adding the new worksheet
After adding the new worksheet and applying the Formula_Today procedure

Find crashes when nothing is found

I have a sheet with a list of course names called Matrix. In another sheet named Courses Date I will have the same courses with the date they were taken.
Example:
The course named Safety Driving will be in Matrix on row 1. In Courses Date there is data from E1:BF1 with the same name. If courses need a refreshment there will be another column named exactly the same name of the course + Refresher (Safety Driving Refresher).
What I am trying to do is to find if a course has a refresher or not. My code returns Run-time error '91': Object variable or With block variable not set if nothing is found.
This is my code:
RefresherColNumber = Range("'Courses Date'!E1:BF1").Find(Range("'Matrix'!" & courseColLetter & "1").Value & " Refresher").Column
Add a check
Dim refreshRange As Range
Set refreshRange = Range("'Courses Date'!E1:BF1").Find(Range("'Matrix'!" & courseColLetter & "1").Value & " Refresher")
If Not refreshRange Is Nothing Then RefresherColNumber = refreshRange.Column
You could use Application.Match to test, with IsError, if value found in row and add 4 to where found to get the column. You should qualify your ranges with the parent worksheet object as well to avoid bugs with implicit activesheet referencing.
Dim matchValue As Variant
matchValue = Application.Match(Range("'Matrix'!" & courseColLetter & "1").Value & " Refresher", Range("'Courses Date'!E1:BF1"), 0)
If Not IsError(matchValue) Then RefresherColNumber = matchValue + 4

Using nested formula in VBA

I'm working on problem that necessitates the use of nested formulas in excel. For eg:
I have a column for errors and one for its analysis
Error Analysis
Enter a valid material number Invalid Material
Eg errors:
Enter a valid material number; The material number 1234 does not
exist.
PO number XYZ does not exist.
VIN number 123 does not exist.
Country of origin AB does not exist.
I have a compendium of such errors and their analyis in the next sheet, and I'm using VLOOKUP in conjuction with FIND to lookup the analysis for the known errors.
=VLOOKUP(LEFT(F2, FIND(" ", F2, FIND(" ", F2) + 1) - 1)&"*", 'Sheet2'!A:B, 2, 0)
What i'm trying to do here is extract the first two words from the error and append a * to it and use it in VLOOKUP.
It would be something like Vlookup "PO number *" in the other sheet and get the analysis for it. Asterisk is because I don 't get the same number daily. And I also know that the extracted first two words of the error will be unique. (I know that error with "Enter a" as the first two words will not appear again).
Now I get errors in the same column so I thought of making a button and writing a code which uses the above formula.
I tried to modify some code off the net, but I'm not getting anywhere with it. I'm totally new to VBA. It'd be great if you can provide a snippet for this. I'll try to replicate the procedure for other needs.
This code seems to be working for now
Sub PopulateAnalysis()
Dim an_row As Long
Dim an_clm As Long
Dim lft As String
Dim st_num As Integer
Dim fin As String
Dim searchStr As String
Dim soughtStr As String
Table1 = Sheet1.Range("F2:F6") 'ErrorColumn from Error table (How do I make the range dynamic??)
Table2 = Sheet5.Range("A1:B6")
an_row = Sheet1.Range("G2").Row ' Populate this column from the analysis table on sheet2
an_clm = Sheet1.Range("G2").Column
For Each cl In Table1
'How do I translate the above formula into VBA statements??
st_num = InStr(InStr(cl, " ") + 1, cl, " ")
lft = left(cl, st_num - 1)
fin = lft & "*"
Sheet1.Cells(an_row, an_clm) = Application.WorksheetFunction.VLookup(fin, Table2, 2, True)
an_row = an_row + 1
Next cl
MsgBox "Done"
End Sub
This should work. You don't need the debug lines of course ;)
Sub PopulateAnalysis()
Dim rngTableWithErrors As Range
Dim rngTableWithAnalysis As Range
Application.ScreenUpdating = False
'set the range for Table with error, Table1 on sheet 1
With Sheets(1) 'change to name of the sheet, more reliable than index num.
Set rngTableWithErrors = .Range("F2:F" & .Cells(.Rows.Count, 6).End(xlUp).Row)
Debug.Print rngTableWithErrors.Address
End With
'set the range for Table with Analysis, Table 2 on sheet 2
With Sheets(2) 'change to name of the sheet, more reliable than index num.
Set rngTableWithAnalysis = .Range("A1:B" & .Cells(.Rows.Count, 2).End(xlUp).Row)
Debug.Print rngTableWithAnalysis.Address
End With
'formula for cell G2
'=VLOOKUP(LEFT(F2;FIND(" ";F2;FIND(" ";F2)+1)- 1)&"*";Sheet2!A1:B23;2; 0)
rngTableWithErrors.Offset(0, 1).FormulaR1C1 = _
"=VLOOKUP(LEFT(R[0]C[-1],FIND("" "",R[0]C[-1],FIND("" "",R[0]C[-1])+1)-1)& ""*"",Sheet2!R1C1:R" & rngTableWithAnalysis.Rows.Count & "C2,2, 0)"
Application.ScreenUpdating = True
MsgBox "Done"
End Sub
Notes
You can notice, that we are setting the upper left cells of ranges manually. It's better practice to somehow find the upper left cells (using Find method is my favorite) and work from there. You never know, how the user will change the worksheet - i.e. add new rows, columns etc.

Manipulating Ranges in Excel - Returning a Value (Error 2029)

I am a quite new to Excel VBA, and I come from a more... traditional programming background (Java, C). I am having issues with passing a Range Object as a parameter in my User-defined function (see below). The idea of my function is to take several parameters to complete a VLOOKUP of a filtered range.
I may have several syntax issues (I am unsure of my return type and my usage of VLOOKUP), and I would appreciate some guidance on this. See results, more information in my code:
Public Function GETVALUE(screen As String, strEvent As String, dataRange As Range, strDate As String) As String
'ASSUMPTION: dataRange has three columns; first column contains lookup values; Second
' column contains dates for filtering; Third column contains return values
Dim result As String
'remove irrelevant dates in dataRange; apply filter
'ASSUMPTION: This process should return a Range that is removes all Rows that does
'not have strDate in the second column
Dim newRange As Range
'RESULT: Returns #VALUE!. I know this is not the typical := syntax I see in many
'examples but this one apparently compiles, so I use it. I comment this line out
'and try to make the other lines below work with dummy parameters or fixed ranges
newRange = dataRange.AutoFilter(2, strDate)
'Now I try to use the newly filtered, "newRange" and use that in my VLOOKUP
'and return it.
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
'I can see an Error 2029 here on Result
GETVALUE = result
'RESULT: Returns #VALUE!
End Function
VLOOKUP ignores any filtering of your data. In other words VLOOKUP will also look in the hidden rows.
I would suggest two alternative approaches:
Copy the visible cells of the filtered range to a new sheet and perform the lookup there:
Set newRange = dataRange.AutoFilter(2, strDate).SpecialCells(xlCellTypeVisible)
set ws = worksheets.Add
ws.Range("A1").Resize(newRange.Rows.Count,newRange.Columns.Count).Value = newRange.Value
etc.
Note that this can not be done in a UDF, you would have to do it in a a Sub.
Store the values in dataRange in a variant array and loop to search for the required value:
Dim arr() as Variant
arr = dataRange.Value
For i = LBound(arr,1) to UBound(arr,1)
If (arr(i,2) = strDate) And (arr(i,1) LIKE "*" & screen & "/" & strEvent & "*"( Then
GETVALUE = arr(i,3)
Exit Function
End If
Next
This I think causes your problem:
result = [VLOOKUP("*" & screen & "/" & strEvent & "*", newRange, 3, False)]
Replace it with this instead:
result = Evaluate("VLOOKUP(*" & screen & "/" & strEvent _
& "*, " & newRange.Address & ", 3, False)")
[] which is shortcut for Evaluate doesn't work on variables.
If it is a direct VLOOKUP like below:
result = [VLOOKUP(D1,Sheet1!$A:$C,3,FALSE)]
it will work. But if you are working with variables as in your example, you have to explicitly state it.
And take note that Evaluate accepts Name argument in a form of string.
So you simply have to concatenate all your strings and then explicitly use Evaluate.
Edit1: Additional Inputs
This will not work as well: newRange = dataRange.AutoFilter(2, strDate).
To pass Objects to a Variable you need to use Set like this.
Set newrange = dataRange.AutoFilter(2, strDate)
On the other hand, AutoFilter method although returning a Range Object fails.
I'm not entirely sure if this can't really be done.
Moving forward, to make your code work, I guess you have to write it this way:
Edit2: Function procedures only returns values, not execute methods
Public Function GETVALUE(screen As String, strEvent As String, rng As Range)
GETVALUE = Evaluate("VLOOKUP(*" & screen & "/" & strEvent & "*, " _
& rng.Address & ", 3, False)")
End Function
To get what you want, use above function in a Sub Procedure.
Sub Test()
Dim dataRange As Range, strDate As String, myresult As String
Set dataRange = Sheet2.Range("A2:E65") 'Assuming Sheet2 as property name.
strDate = "WhateverDateString"
dataRange.AutoFilter 2, strDate
myresult = GETVALUE("String1", "String2", dataRange)
End Sub
Btw, for a faster and less complex way of doing this, try what Portland posted.
Basically you must write :
Getvalue = Application.VLookup( StringVar, RangeVar, ColumnNumberVar)
Vlookup needs your data to be previously ordered in alphabetical order, or it doesn't work.
Excel Developers's approach is a good one too, using a VBA Array.
I can also point the VBA functions FIND, and MATCH, wich will get you the row of searched data, and then you can pull what you need from the 3rd column of that row.
Wichever is faster depends of the size of your range.

VBA Insert function VLOOKUP into range,lookup range in other workbook

VBA Insert function VLOOKUP into range,lookup range in other workbook. The file containing the lookup table is achieved using filename_AcctMgr = Application.GetOpenFilename(, , "Select Acct Mgr File", "Select"), then opening the file. Let's call this workbook2.
In workbook1 I am adding the VLOOKUP formula into "F2" and looking up Column "A" values in workbook2, columns A:C. I Then copy the formula to all rows of column "F".
I cannot find the syntax required to properly reference the workbook2 range in columns A:C.
ActiveCell.Formula = _
"=VLOOKUP(activecell.offset(0,-5).address,'ws.name'!A:C,3,FALSE)"
Can anyone suggest the proper syntax?
Try this:
Range("F2").Resize(10).Formula = "=VLOOKUP(A2,[Book2]Sheet1!$A:$C,3,FALSE)"
Or
Range("F2:F10").Formula = "=VLOOKUP(A2,[Book2]Sheet1!$A:$C,3,FALSE)"
EDIT: Sorry I forgot the piece about the filename as a variable:
Dim MyFile As String
Dim vSplit As Variant
Dim iCnt As Integer
MyFile = Application.GetOpenFilename(, , "Select Acct Mgr File", "Select")
vSplit = Split(MyFile, "\")
iCnt = UBound(vSplit)
vSplit(iCnt) = "[" & vSplit(iCnt) & "]"
MyFile = Join(vSplit, "\")
Range("F2:F10").Formula = "=VLOOKUP(A2,'" & MyFile & "Sheet1'!$A:$C,3,FALSE)"
You will need to add error handling in case someone clicks cancel. Also I doubt you want to add the formula to all rows in column f so just define the range you want. My examples is rows 2 to 10.
I am assuming you want the name of the sheet / range to be in a variable, rather than hard-coded. As it it, you have the name of the variable in the middle of your string, but it will be treated as a string, not a variable containing a string.
I suggest that you do something like the following:
Dim sheetName, lookupFrom, myRange ' always declare your variables
sheetName = "This is the sheet name" ' note I added some spaces to make it challenging
lookupFrom = ActiveCell.Offset(0, -5).address
myRange = "'" & sheetName & "'!A:C" ' putting quotes around the string so it's always valid
ActiveCell.Formula = "=VLOOKUP(" & lookupFrom & "," & myRange & ", 3, FALSE)"
You can of course do this all at once - it just gets messy to look at:
ActiveCell.Formula = "=VLOOKUP(" & ActiveCell.Offset(0, -5).Address & ", '" & sheetName & "'!A:C, 3, TRUE)"
Further note - the sheetName can of course contain the name of the other workbook - but you need name of workbook AND sheet... so
sheetName = "[Book2]Sheet1"
would be fine.
In your example you used ws.name (without proper quoting) - but that would not have given you the full path since you need both the workbook and the worksheet name to make sure you reference the right data. Better be explicit - if Excel can make the wrong assumptions about what you want, it will - and you will be left scratching your head...
Since you actually showed you had opened the book, you have the name of the file (the workbook) in your variable filename_AcctMgr. Then you should be able to use:
sheetName = "[" & filename_acctMgr & "]Sheet1"
and take it from there.

Resources