VBA Subscript out of range, name resolution problem? - excel

Trying to write a VBA function that will return the column number given the header cell string and the worksheet name but I get the Subscript out of range error.
Here is the function:
Public Function namedColumnNo(heading As String, shtName As String) As Long
' Return the column number with named header text'
' on given worksheet.
Dim r As Range
Dim wks As Worksheet
Debug.Print shtName
'Exit Function
Set wks = Sheets(shtName)
wks.Range("1:1").Select
With wks
r = .Range("1:1").Find(heading, LookIn:=xlValue)
If r Is Nothing Then
namedColumnNo = -1
Else: namedColumnNo = r.Column
End If
End With
End Function
I am using this test sub to call the funtion:
Public Sub getCol()
Debug.Print "Find MidTemp on " & DataSht.RawDataSht
Debug.Print "Col " & namedColumnNo("MidTemp", DataSht.RawDataSht)
End Sub
I have a user defined type DataSht where I have variables to name worksheets e.g.
Public Type dataShtNames
HeaderSht As String
RawDataSht As String
ResultDataSht As String
End Type
Public DataSht As dataShtNames
With the Exit Function statement uncommented the variables resolve OK with the debug.print statements I get
Find MidTemp on RawData
RawData:MidTemp
Col 0
Leaving the function to run through the error occurs at
Set wks = Sheets(shtName)
If I replace the argument shtName with the actual sheet name as a string "RawData", then the error moves down to the line using the second argument heading. If I substitute a the parameter with a string here the error persists.
Is there something I am missing here? Some help will be much appreciated.

Sadly can't comment, but you're actually getting the out of range error because it should be LookIn:=xlValues where you have LookIn:=xlValue
As #Mathieu indicates, you'll need to fix add Set r = Find(heading, LookIn:=xlValues) to set the range to the value returned.
As a side note-you should drop the selection. Its not doing anything for you.
With wks.Range("1:1")
Set r = .Find(heading, LookIn:=xlValues)
If r Is Nothing Then
namedColumnNo = -1
Else: namedColumnNo = r.Column
End If
End With

Related

VBA: Use "Find" to get the row of a value / fixing object variable errors

I need to use the "find" function to get the row of a value, where that value is defined from another worksheet, but I keep getting object-variable not defined errors even when I try defining everything.
I was initially able to get the code below to work. (I paste the "test" version of the code that only uses a small range of 20 cells. The result is placed in cell "A1" to make sure it works correctly in testing.)
X = Range("D6").Value
Range("A1").Value = Range("D2:D20").Find(X).Row
But when I use this line in a large data set, I keep getting error 91 ("Object variable or With block variable not set").
So instead, I try code that defines everything more explicitly, but for the life of me, I can't get this code to work even on the small test set. (I also include a few lines that I've tried but that also produce errors.)
Sub Test3()
Sheets("2017").Select
Dim X As Long
X = Range("D6").Value
Dim ws As Worksheet
Set ws = Worksheets("2017")
Dim SearchRange As Range
Dim FindRow As Range
Set SearchRange = ws.Range("D2", ws.Range("D20").End(xlUp))
Set FindRow = SearchRange.Find(X, LookIn:=xlValues, lookat:=xlWhole)
If Not FindRow Is Nothing Then ReturnRowNumber = FindRow.Row
'Range("A1").Value = FindRow 'Running this row gives application-defined error 1004
Dim i_2017 As Long
i_2017 = FindRow.Rows(1) 'All of these rows give object variable not set error 91
'i_2017 = FindRow
'i_2017 = FindRow.Rows.Item(1)
Range("A1").Value = i_2017 'Check that the sub returns 6
End Sub
If I try to return "FindRow" in cell "A1", I get error 1004 ("Application-defined or object-defined error")
If I try to store "FindRow" as another variable or use the "Item" property, I get error 91 ("Object variable or With block variable not set")
You need to enclose all of your code that assumes FindRow has actually been found inside your If Not FindRow is Nothing statement.
Sub Test3()
Dim ws As Worksheet: Set ws = ThisWorkbook.Sheets("2017")
Dim Found As Range, ReturnRowNumber As Long
Set Found = ws.Range("D:D").Find (ws.Range("D6"), LookIn:=xlValues, LookAt:=xlxWhole)
If Not Found Is Nothing Then
'All code that assumes your value is found goes HERE
ReturnRowNumber = Found.Row
Range("A1").Value = Found
Else
'All code that assumed your value is NOT found goes HERE
MsgBox ws.Range("2017") & ": Not Found in Range"
End If
End Sub

Is there a way to reassign a Range variable to a different range?

I am very new to VBA, having started programming it yesterday. I am writing a data processing program which requires keeping track of two cells, one on each spreadsheet. The code which reproduces the errors I am experiencing is below. When I call the sub moveCell() in sub Processor(), nothing happens to DIRow and DIColumn, and the code spits out error 1004 at the line indicated. I have tried using DICell = DICell.Offset(), but it returns the same error.
How can I redefine a Range variable to be a different cell?
'<<Main Processor Code>>'
Sub Processor()
Dim PDRow As Integer
Dim PDColumn As Integer
Dim DIRow As Integer
Dim DIColumn As Integer
PDRow = 1
PDColumn = 1
DIRow = 1
DIColumn = 1
Dim PDCell As Range
Dim DICell As Range
Set PDCell = Worksheets("Processed Data").Cells(PDRow, PDColumn)
Set DICell = Worksheets("Data Input").Cells(DIRow, DIColumn)
Call moveCell(2, 0, "Data Input")
End Sub
'<<Function which moves the cell which defines the range>>'
Sub moveCell(r As Integer, c As Integer, sheet As String)
If sheet = "Processed Data" Then
PDRow = PDRow + r
PDColumn = PDColumn + c
Set PDCell = Worksheets("Data Input").Cells(PDRow, PDColumn)
ElseIf sheet = "Data Input" Then
DIRow = DIRow + r '<<<<<<This line does nothing to DIRow's value
DIColumn = DIColumn + c
Set DICell = Worksheets("Data Input").Cells(DIRow, DIColumn) '<<<<<<This line causes error 1004
End If
End Sub
As far as I can tell, you could instead use a quick Function instead. There doesn't seem to be any difference in your If statement results in the moveCell() function, except which worksheet you're using.
We can make this simpler by referring to the Range you're passing to moveCell.
Option Explicit ' forces you to declare all variables
Sub something()
Dim PDCell As Range
Set PDCell = Worksheets("Processed Data").Cells(1, 1)
Dim DICell As Range
Set DICell = Worksheets("Data Input").Cells(1, 1)
PDCell.Select ' can remove
Set PDCell = moveCell(2, 0, PDCell, PDCell.Worksheet.Name)
PDCell.Select ' can remove
Worksheets(DICell.Worksheet.Name).Activate ' can remove
DICell.Select ' can remove
Set DICell = moveCell(5, 0, DICell, DICell.Worksheet.Name)
DICell.Select ' can remove
End Sub
Function moveCell(rowsToMove As Long, colsToMove As Long, cel As Range, ws As String) As Range
Set moveCell = Worksheets(ws).Cells(cel.Row + rowsToMove, cel.Column + colsToMove)
End Function
I've included some rows you don't need (which I've marked with a comment afterwards), but that will show you how the routine works. You can step through with F8 to help see it step-by-step.
Edit: Although, you don't need a separate function at all. Just use OFFSET().
Set PDCell = ...whatever originally
Set PDCell = PDCell.Offset([rows],[cols])

Index match in VBA referencing a table

I want to update a line in my table based on a cell in another sheet, and to that end I intend to use the index match function. When I run the code below I get the error that it cannot get the property of the match function class.
What is the correct syntax in this regard?
Sub Update_Customer()
' Declarations
Dim rng as listobject
Dim wf as application.worksheetfunction
Dim cs_sht as string
Set rng = Sheets(1).ListObjects("Table_Customer")
Set ws = ThisWorkbook.ActiveSheet
cs_sht = ws.Name
' ERROR RUNNING THIS LINE vvvvv
wf.Index(rng.ListColumns("Firstname"), wf.Match(cs_sht, rng.ListColumns("Customer ID"), 0)) = ws.Range("C_Firstname").Value
End Sub
Excel functions need to be nested, because a cell's value needs to be parsed as a single step.
VBA code doesn't need to do that. VBA instructions work best and are easier to debug when you split them and make them do as little work as possible.
So instead of this:
wf.Index(rng.ListColumns("Firstname"), wf.Match(cs_sht, rng.ListColumns("Customer ID"), 0))
Split it up:
Dim matchResult As Long
matchResult = WorksheetFunction.Match(cs_sht, rng.ListColumns("Customer ID").DataBodyRange, 0)
Dim indexResult As Variant
indexResult = WorksheetFunction.Index(rng.ListColumns("FirstName").DataBodyRange, matchResult)
Note that you'll get a run-time error if either function fails to find what it's looking for. Use On Error to handle that case:
On Error GoTo CleanFail
Dim matchResult As Long
matchResult = WorksheetFunction.Match(...)
...
Exit Sub
CleanFail:
MsgBox "Could not find record for '" & cs_sht & "'." & vbNewLine & Err.Description
End Sub
Get rid of wf. There's no use to copy object references of objects that are already global. The fewer global variables you use, the better.
if the first name changes I can update the table to match the new name from my worksheet
You can't just assign the indexResult to a new value. The indexResult isn't holding a reference to any cell, it's just the result of the INDEX worksheet function. You need to use that result to get the cell you want to modify:
Dim targetCell As Range
Set targetCell = rng.ListColumns("FirstName").DataBodyRange.Cells(indexResult)
targetCell.Value = ws.Range("C_Firstname").Value

Get defined name in excel worksheet from passing range

I'm trying to populate an excel file using defined names. What I like to do now is that once I move to a cell e.g. Worksheet1!$A$8, I want to retrieve the defined name for that cell which tells me what is the data needed. Right now this is what I got and only give me ....$A$8, what I expect is PROD_CATEGORY as this is what I defined the name to be for that cell. Really appreciate if anyone can help.
WorkSheet.Range[ColNumToAlpha(CurrCol)+IntToStr(HRow)].Name
thanks,
Here's a function that will return all the range names containing the range you pass to the function. You could, for instance, pass WorkSheet.Range[ColNumToAlpha(CurrCol)+IntToStr(HRow)] to this function.
Function TellNamedRanges(ByVal Target As Range) As String
Dim NamedRange As Name
Dim FoundOne As Boolean
Dim RangesFound As String
FoundOne = False
RangesFound = "Found these ranges: "
For Each NamedRange In ThisWorkbook.Names
If Not Application.Intersect(Target, Range(NamedRange.RefersTo)) Is Nothing Then
FoundOne = True
RangesFound = RangesFound & NamedRange.Name & " "
End If
Next NamedRange
If FoundOne = False Then
TellNamedRanges = RangesFound & "none found"
Else
TellNamedRanges = RangesFound
End If
End Function

Excel VBA: Why can't I get a range from a worksheet I passed into a function?

I'm trying to use a helper function to get a range to store it into a variant, but I'm running into some issues.
At first, I just simply tried the following:
Function GetRange(RangeLetter As String, Optional LastUniqueLine As Long = 1048576) As Varient
Static LastUniqueLineStr As String
If LastUniqueLineStr = "" Then
LastUniqueLineStr = LastUniqueLine
End If
Set GetRange = Range(RangeLetter + "2:" + LastUniqueLineStr)
End Function
But that didn't seem to work. Range() seemed to be out of scope here or something, so I figured I had to pass in the worksheet to get it working:
Function GetRange(RangeLetter As String, Optional LastUniqueLine As Long = 1048576, Optional ActiveSheet As Worksheet) As Variant
Static LastUniqueLineStr As String
Static CurrentSheet As Worksheet
'If CurrentSheet = Nothing Then
Set CurrentSheet = ActiveSheet
'End If
If LastUniqueLineStr = "" Then
LastUniqueLineStr = LastUniqueLine
End If
Set GetRange = CurrentSheet.Range(RangeLetter + "2:" + LastUniqueLineStr) ' This is the line where I get the error.
End Function
And that's not working either. I get the following error:
Run-time error '1004':
Method 'Range' of object 'Worksheet' failed
How do I get the range I want out of this when I call it?
Try:
GetRange = Range(RangeLetter + "2:" + RangeLetter + LastUniqueLineStr).Value
You were missing a RangeLetter which was resulting in a malformed address. Also, use the .Value property to return a variant/array, and omit the Set keyword.
I continue to get the error when qualifying as CurrentSheet.Range... there is no ActiveSheet within the context of the Function, so you could pass a worksheet as a variable:
Sub Test()
Dim var As Variant
var = GetRange(ActiveSheet, "A")
End Sub
Function GetRange(sh As Worksheet, RangeLetter As String, Optional LastUniqueLine As Long = 1048576, Optional ActiveSheet As Worksheet) As Variant
Static LastUniqueLineStr As String
Dim myRange As Range
If LastUniqueLineStr = "" Then
LastUniqueLineStr = LastUniqueLine
End If
Set myRange = sh.Range(RangeLetter + "2:" + RangeLetter + LastUniqueLineStr)
GetRange = myRange.Value ' This is the line where I get the error.
End Function
It looks to me like the error is in the line where your setting Your "GetRange" variable. It looks like the result of .Range(RangeLetter + "2:" + Last....) would create a range with an address of "a2:#" which will fail. A range in that format needs to be "a2:e7" or similar. Your range reference has to be symmetrical. "RC:RC", "R:R", or "C:C". Hope that helps.

Resources