Problems with VLOOKUP in VBA - vba

All,
I'm trying to use vlookup in a simple VBA function, but it is continually returning #VALUE!
Here is the code:
Public Function getAreaName(UBR As Integer) As String
Dim result As String
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Sheets("UBR Report")
' check level 3 then 2 then 4 then 5
result = Application.WorksheetFunction.VLookup(UBR, sheet.Range("UBRLookup"), Application.WorksheetFunction.Column(sheet.Range("UBRLookup[Level 3]")), False)
getAreaName = result
End Function
Any thoughts?

I'm not quite sure what you're trying to do with the "UBRLookup[Level 3]" reference, but as Joseph has pointed out, that's the bit that you're doing wrong.
[ is not a valid character for a named range in Excel.
The column that you're referencing needs to be a numeric value, the offset from the start of the table-array you've defined as your named range.
The below should work, provided the column you want to pull out is the second column in your named range (e.g. what you're referring to as [level 3] is in the second column).
Public Function getAreaName(UBR As Integer) As String
Dim result As String
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Sheets("UBR Report")
result = Application.WorksheetFunction.VLookup(UBR, sheet.Range("UBRLookup"), 2, False)
getAreaName = result
End Function
Update:
I've had a look at Excel 2007 and from what I can see the column function isn't exposed as an Application.WorksheetFunction.
You can use it on the sheet with =Column(D4), but when trying to autocomplete within the vba editor, the function isn't there. This may be due to a difference in versions, so I'll ignore that for now.
It still definitely seems like you're mis-using the third argument. If you really don't want to use the number reference we need to find out where the function is going wrong.
A few tests along the lines of
Debug.Print Application.WorksheetFunction.Column(D4)
Debug.Print sheet.Range("UBRLookup[Level 3]")
should hopefully help to show you exactly where it's going wrong - I believe that it will object to both of the above, but if it returns some useful information then we may be a step closer to your solution.

Break you function up into more pieces. Then debug it and make sure every piece is set up the way you expect.
Example:
Public Function getAreaName(UBR As Integer) As String
Dim result As String
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Sheets("UBR Report")
Dim range as Range = sheet.Range("UBRLookup")
Dim column as Column = Application.WorksheetFunction
.Column(sheet.Range("UBRLookup[Level 3]"))
result = Application.WorksheetFunction.VLookup(UBR, range, column, False)
getAreaName = result
End Function
In fact, just by doing that I noticed something weird. You use a range in two different places, but in one place you're looking for UBRLookup, and in another you're looking for UBRLookup[Level 3], is that correct?

I am disturbed by
Dim column as Column =
Application.WorksheetFunction.Column(sheet.Range("UBRLookup[Level 3]"))
You should Dim column as long, I think, and maybe use a variable name that's not to be confused with a property, like lngCol.

This part: sheet.Range("UBRLookup[Level 3]") is suspect as "UBRLookup[Level 3]" is not a valid range name.

Related

VBA Range change selection, value doesn't change

I have a range variable (called Constr) that is based on data that looks like this
Type Bound1 Bound2 Var1 Var2
X 1 2 3 4
Y 1 2 3 4
--
Z 1 2 3 4
I now use this procedure to change the selection to only the entries before the '--'
Sub Adjust_Selection(which As String, what_in As String, columns As Integer)
Dim row_nr_start As Integer
Dim row_nr_end As Integer
Dim row_nr_delta As Integer
Sheets("Main").Select
row_nr_start = Range(which).Find(what:=what_in, LookIn:=xlValues, LookAt:=xlWhole).Row
row_nr_end = Range(which).Find(what:="--", LookIn:=xlValues, LookAt:=xlWhole).Row
row_nr_delta = row_nr_end - row_nr_start
Range(which).Resize(row_nr_delta, columns).Select
This works and I can see that the selection changes, if I now call it using
Call Adjust_Selection("Constr", "Type", 5)
myitem("Constraints") = Range("Constr").Value
myitem is of type
Dim myitem As New Scripting.Dictionary
however when I access the value it still has everything in it. How can I update the value to only the first few lines up until the '--'?
You are calling Adjust_Selection with the named range Constr and afterwards refer to the named range Constraints. So, of course the result is different because you are referring to two different named ranges.
Furthermore, the named range Constr is not altered. It is merely used as a starting point and then a sub-set is Selected. But by selecting something you are not changing a named range (especially not a differently named range).
So, I am guessing that this is what you are searching for:
Call Adjust_Selection("Constr", "Type", 5)
ThisWorkbook.Names.Add Name:="Constraints", RefersTo:=Selection
myitem("Constraints") = Range("Constraints").Value
Note, that the selection of Adjust_Selection is now "saved" in the new named range Constraints and then myitem is being assigned this named range which is limited to the (correct) selection. Hence, the resulting variable (being a dictionary) contains all elements without the --.
Hi ThatQuantDude, I don't quite understand your question even after trying it out on my own. Based on the examples you gave, I assumed you want to store the selected range data into "Constraints" key? Apart from this, your sub function for selecting the range is working fine.
Call Adjust_Selection("Constr", "Type", 5)
myitem("Constraints") = Range("Constraints").Value
Appreciate if you could elaborate it further so I can better understand what you are trying to do? Thanks.
Range.Resize is a function. It will not change the range; it returns a new one. You just happen to select it, which isn't necessary. Turn your sub into a function returning the result of Range.Resize, and use this function directly on the right hand side of your assignment.
Note that you're not using the same name for your range in both lines of code, which I assume is a typo.

Match Function in Specific Column Excel VBA

I'm trying to write a program in VBA for Excel 2011 that can search a column (which column that is is determined by another variable) for the number 1 so that it knows where to start an iteration.
Say that the number of the column is given by colnumvar. The only way I can think of is the Match function, which led me to write the following:
Dim rowvar As Integer
rowvar = WorksheetFunction.Match(1,Range(Cells(1,colnumvar),Cells(1000,colnumvar)),0)
This gave me an error, however. After playing around with it some more, I realized that it must not accept the Cells([row],[col]) way of doing it, but rather wants something like Range("A1:A100"). Unfortunately, I can't do it that way, since the program is figuring out what column to look in. Any help for figuring out how to get past this would be greatly appreciated!
What you mean to do is better served with Range.Find.
Dim rngtrg As Range, rngsrc As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Set rngsrc = ws.Range(ws.Cells(1,colnumvar),ws.Cells(1000,colnumvar))
Set rngtrg = rngsrc.Find(1,...)
rowvar = rngtrg.Row
this easy function retreive the positoin of that you find
Function rowvar(ByRef c As Integer) As Integer
Dim keySrc As Integer
keySrc = 22 'wath you want to find
rowvar = WorksheetFunction.Match(keySrc, Range(Cells(1, c), Cells(1000, c)), 0)
End Function
use with rowvar(x)

VBA: Syntax for dynamic CountIf Ranges

I'll do my best to try and explain my problem, but it's still a bit fuzzy in my mind so this might not be as clear as it should be, for which I apologize in advance.
Here's the part of my code I'm having trouble with:
If Application.WorksheetFunction.countif(Range("D:D"), Cells(x, firstcolumn).Value) _
And Application.WorksheetFunction.countif(Range("F:F"), Cells(x, firstcolumn).Value) _
And Application.WorksheetFunction.countif(Range("H:H"), Cells(x, firstcolumn).Value) Then
The idea behind this project is to check if the values in "Cells(x, firstcolumn)" are present in columns D, F and H at the same time, and then paste the values somewhere else.
However the number of columns to check for the "Cells(x, firstcolumn)" values could be changed, so values would need to be checked in any number of columns (2, 10 etc). My code works perfectly for the specified Ranges but if one is missing or more are added then it stops working.
The columns to check against are always offset by 2 from the firstcolumn and firstcolumn is always B, it will be checked against D, F, H and so on while columns C,E,G etc have other data not relevant for this part.
My best guess is to have the countif Ranges changed dynamically but I'm at a loss of when and how this should be done...
Could anyone point me towards the right direction in order to achieve this? I can post the full code if needed.
Cheers!
You need to extract a function here. Something like this:
Private Function IsPresentInRange(ByVal source As Range, ByVal value As Variant) As Boolean
IsPresentInRange = Application.WorksheetFunction.CountIf(source, value) > 0
End Function
And then you need a way to figure out what ranges you need to give it for a source parameter - that can be a function of its own, or you can hard-code them somewhere; basically you want to have a concept of a group of ranges to call that function with - this would be the simplest:
Private Function GetSourceRanges() As Collection
Dim result As New Collection
result.Add Range("D:D")
result.Add Range("F:F")
result.Add Range("H:H")
'maintain this list here
Set GetSourceRanges = result
End Function
Ideally you would have some logic coded there, so that you don't need to manually add ranges to that collection every time.
And then you can just iterate these ranges and determine if you get a count > 0 for all of them:
Dim sources As Collection
Set sources = GetSourceRanges
Dim result As Boolean
result = True
Dim sourceRange As Range
For Each sourceRange In sources
result = result And IsPresentInRange(sourceRange, Cells(x, firstcolumn).Value)
Next
If result Then
' whatever you had in that If block
End If

VBA Evaluate function and array formula returning range of values

Suppose I want to use Evaluate function to evaluate an array formula which returns a range of values. For example I try to get index (using Excel function MATCH) in an ordered list in Sheet2!A:A for each values in Sheet1!A:A. And I want to put the indices in column B.
Dim sh as Worksheet
Set sh = Sheets("Sheet1")
sh.Range("B1:B10").Value = sh.Evaluate("=MATCH(A1:A10,Sheet2!A:A)")
Whan I run the code, I get a column of repeated values - the values are equal to the index of the first element. This is not correct.
When I try the same by putting array formula in the worksheet {=MATCH(A1:A10,Sheet2!A:A)}, it works without problems and returns the correct index for every element.
So my question: how to use Evaluate function returning a whole range of values?
Interesting issue. I was not able to get the MATCH function to return an array using VBA Evaluate. However, the following modification seems to work, using the zero (0) for the row argument in the index function returns all of the rows. Note also that I added the match_type argument to the Match function.
sh.Range("B1:B10").Value = sh.Evaluate("=INDEX(MATCH(A1:A10,Sheet2!A:A,0),0,1)")
If Evaluate() does not make you happy, then:
Sub marine()
Dim sh As Worksheet, r As Range
Set sh = Sheets("Sheet1")
Set r = sh.Range("B1:B10")
r.FormulaArray = "=MATCH(A$1:A$10,Sheet2!A:A,0)"
r.Copy
r.PasteSpecial xlPasteValues
End Sub

Counting Rows/Columns of Selected Range Error

I am trying to determine if a selected range is within a set area... This toggles Copy/Paste restrictions in the spreadsheet. I have figured it out, I think, but I'm getting a run-time error 6 (Overflow) if you select an entire row or column. This is what I've got..
Function BETWEENROWS(ByVal Selected As Range, ByVal Min As Double, ByVal Max As Double) As Boolean
Dim LastRow As Integer
LastRow = Selected.Row + Selected.Rows.Count - 1
If BETWEEN(Min, Selected.Row, Max) = True And BETWEEN(Min, LastRow, Max) = True Then
BETWEENROWS = True
Else
BETWEENROWS = False
End If
End Function
There is one for columns BETWEENCOLUMNS as well and the function BETWEEN just returns True/False if a given number is between a min and max value.
This is working great, however, if an entire row/column is selected it's throwing an error and I'm not too familiar with VBA and the only way that I know of bypassing the error is with On Error Resume Next but that seems like I'm putting a bandaid on it and would like to figure out how to fix it another way.
Your LastRow variable is not the correct type for a number as large as the max columns/rows of the spreadsheet. Change the type to Long:
Dim LastRow As Long
You are getting an overflow error because you have made the LastRow variable an integer. Since there are more rows in an entire column then can fit in an integer variable, it triggers the overflow. You could fix this by changing the LastRow variable to be type Long
However, rather then comparing row values you may want to look into the Intersect() function. Given two (or more) ranges it will return the range object that represents the intersection of the two ranges. You could then check that intersection. If they don't intersect the range object will be Nothing. There is a good tutorial for this function at ozgrid.com
UPDATE
Here is the code to ensure range intersects fully using the Intersect() function
'// Run a test here to make sure Intersect does not return Nothing
If (TestRNG.Count <= ISectRNG.Count) And (Intersect(TestRNG, ISectRNG).Count = TestRNG.Count) Then
'// All of TestRNG falls within ISectRNG
End If