Using named range in VBA function for VLOOKUP

Mike Rockétt picture Mike Rockétt · May 6, 2014 · Viewed 19.5k times · Source

I have a the following on my worksheet:

  • A cell that shows a currency [in A1]
  • A range of cells (two columns, one for the currency, and the other for a corresponding commission percentage) [defined as/named RANGE, and scoped to the worksheet]
  • A cell that [is trying] to determine the calculated commission percentage based on A1 and RANGE

I then have a VBA function called Calculate, as follows:

Function Calculate(LookupValue As Double, LookupRange As Range) As Double
    Calculate = [VLOOKUP(LookupValue, LookupRange, 2)]
End Function

The cell that determines the percentage has the following:

=Calculate(A1, RANGE)

Problem is that the cell just returns #VALUE!...

Any idea what I could be doing wrong?

I have tried several things like type-hinting to Range(), passing LookupRange.Value2 to VLOOKUP, etc, none of which worked.

I have also tried to debug, noting that LookupRange does actually contain the range required in Value2, which is is why I tried to pass it to the function.

Side Note: The function and layout mentioned above is just a dummy - the actual function is somewhat more complex as it relies on negotiated rates, monthly margins, etc. This is why I'm using VBA in the first place. I know that I'm doing something wrong with the lookup, as it is the only thing that seems to be failing within the function - everything else corresponds and calculates.

Answer

Dmitry Pavliv picture Dmitry Pavliv · May 6, 2014

From MSDN:

The advantage of using square brackets is that the code is shorter. The advantage of using Evaluate is that the argument is a string, so you can either construct the string in your code or use a Visual Basic variable.

in other words you can use

Calculate = [VLOOKUP(3, A1:B100, 2)]

but you can not use

LookupValue = 3
LookupRange = Range("A1:B100")
'or
'LookupRange = "A1:B100"
Calculate = [VLOOKUP(LookupValue, LookupRange, 2)]

What you can do is:

Option 1:

Function Calculate(LookupValue As Double, LookupRange As Range) As Double
    Calculate = Evaluate("VLOOKUP(" & LookupValue & "," & LookupRange.Address & ", 2")
End Function

or better:

Function Calculate(LookupValue As Double, LookupRange As Range) As Double
    Calculate = Evaluate("VLOOKUP(" & LookupValue & ",'" & _
        LookupRange.Parent.Name & "'!" & LookupRange.Address & ", 2")
End Function

However I suggest:

Option 2:

Function Calculate(LookupValue As Double, LookupRange As Range) As Double
    Calculate = Application.VLookup(LookupValue, LookupRange, 2)
End Function

I hope you know about meaning of 4th parameter:

If TRUE or omitted, an exact or approximate match is returned. If an exact match is not found, the next largest value that is less than lookup_value is returned. The values in the first column of table_array must be placed in ascending sort order; otherwise, VLOOKUP may not give the correct value. You can put the values in ascending order by choosing the Sort command from the Data menu and selecting Ascending.

Btw, Calculate is not good name for UDF, since VBA already has function Application.Calculate. I'd rename it to avoid confusion.