Only some lines throwing "Object variable or with variable not set" error when using .Find method - vba

(I know this sounds like a repeat question, but I've searched everywhere for this, and I couldn't find anything about this)
I'm trying to automate a tax filing process at work. Essentially, I'm reading off several worksheets and populating relevant fields into a master mapping sheet. However (and this is the strange part), when I first coded everything out, there were no errors and I managed to find everything. After saving and reopening the workbook though, I keep getting this error and only for certain lines (9 occurences out of the 57 times I call the .find method in total).
I know the error means that the .find method couldn't find what I was looking for, but I know the field exists since it worked perfectly well before I closed and reopened the workbook, and it works for the rest of the searches. Does anyone have any ideas?
I've checked my macro security settings (everything is enabled), and I know the .find method isn't retrieving anything (I put in "if ___ is nothing" statements to verify, and the affected lines all return nothing when called), but I know the fields I'm searching for exist in their respective sheets.
Here's one of the lines throwing the error:
Range("C7").Select
Set currentRowReference = Worksheets("Stat A").Range("B1:B999").Find("Current year tax losses", LookIn:=xlValues, lookat:=xlPart)
firstCalculation = Worksheets("Stat A").Cells(currentRowReference.Row - 2, "E")
Set currentRowReference = Worksheets("Stat A").Range("A1:A999").Find("Unabsorbed capital allowances c/f", LookIn:=xlValues, lookat:=xlPart)
secondCalculation = Worksheets("Stat A").Cells(currentRowReference.Row - 1, "E")
ActiveCell.FormulaR1C1 = firstCalculation - secondCalculation
Any help or ideas will be much appreciated!
Edit: I've added a larger portion of my code, and a screenshot of the sheet it's supposed to read from. In the code above, firstCalculation successfully computes, while secondCalculation throws the error.
Screenshot of source sheet. Specifically, it's supposed to read A31

since you have to deal with merged cells, you'd better use a function on your own like the following:
Function MyRowFind(rng As Range, textToFind As String) As Long
Dim cell As Range
MyRowFind = -1
For Each cell In rng.SpecialCells(xlCellTypeConstants, xlTextValues)
If InStr(cell.value, textToFind) > 0 Then
MyRowFind = cell.Row
Exit Function
End If
Next cell
End Function
to be used in your main sub as follows:
Sub main()
Dim currentRowReferenceRow As Long
currentRowReferenceRow = MyRowFind(Worksheets("Stat A").Range("A1:A999"), "Unabsorbed capital allowances c/f")
If currentRowReferenceRow > 0 Then
' code
End If
End Sub

Related

Inserting, searching, copying, pasting across 2 spreadsheets in Excel

I thought I'd be able to figure this out based off an analysis of similar code we had written for something else, but code isn't my forte.
Diving into VBA without guidance has proved to be too daunting for me to proceed.
Forgive my terminology if I use the wrong language, I'm happy to learn and be corrected.
This shouldn't be difficult for someone that knows what they're doing, I just don't at all.
I'm trying to create a macro enabled workbook that does the following:
Open "Data.csv" from a folder called "Data" in the same directory as the macro. We'll call this workbook A - wbA.
Insert a column on wbA after Column C titled "Group Image Name." This Column D is where we want data to end up.
Open "Groups.csv" from a folder called "Groups" in the same directory as the macro. We'll call this workbook B - wkB.
(This next part needs to be a loop that starts at C1 on wbA and proceeds down the column until the end of the spreadsheet)
Copy value from selected cell in Column C on wbA
Search Column C on wbB for copied value. When found, move selection over to corresponding cell in Column A. (If C2, then A2)
Copy contents of the column A value from wbB to column D cell on wbA that corresponds to the original starting point on wbA.
Basically in plain language: Search for Column C contents from wbA on Column C of wbB. When found, move to Column A of same cell # on wbB, copy contents, and paste into cell # of Column D on wbA that corresponds to cell # of starting point from Column C.
I hope that's clear; please feel free to ask for more details if necessary. Thanks for anyone's help in advance!
Here is my terrible code I'm working with at the moment:
Sub OpenDataAddGroupImageTitleColumn()
ChDir "C:\[RealCodeHasCorrectFilepath..]\Desktop\TEST"
Workbooks.Open Filename:="C:\[RealCodeHasCorrectFilepath..]\Desktop\TEST\DATA.csv"
Columns("D:D").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Cells.Select
Cells.EntireColumn.AutoFit
Range("D1").Select
ActiveCell.FormulaR1C1 = "Group Image Title"
Range("C2").Select
'Variables for storing Row, Column Location, and Value here
Dim GROUPNAME As String
Dim RowLocationX As Long
Dim ColumnLocationZ As Integer
GROUPNAME = ActiveCell.Value
RowLocationX = ActiveCell.Row
ColumnLocationZ = ActiveCell.Column
Workbooks.Open Filename:="C:\[RealCodeHasCorrectFilepath..]\Desktop\GROUPS.csv"
Columns("C:C").Select
Selection.Find(What:="GROUPNAME", After:=ActiveCell, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False).Activate
End Sub
The current snag I can't figure out after much googling is this: Is it possible to search for my variable GROUPNAME using the Find/Replace feature? Or should I be taking a different approach using something involving copying to clipboard?
The first problems that I see are:
1) You don't put functions in the middle of sub routines. If you want a function, you put it on its own as such:
Sub MySub()
'Do Stuff
x = myFunction()
debug.print x
End Sub
Function MyFunction() As String
'Do Stuff
MyFunction = "Test"
End Function
2) The code you have provided won't compile for reason 1 and also because you have ended the Sub with "End Function" instead of End Sub. Try running the code and you will get error messages. Then you can research the error message and try to fix it. Further, if you don't get error messages, you can step through the code to make sure it is working the way you intend it to.
3) The very first line isn't going to work. You need to use the ENTIRE path of the file, even if it is in the same folder as the file you currently have open. There are ways to get the directory of the file you currently have open (google will surely show you many of them), and you could just append the filename to the directory of the file you have open.
4) You want a loop, but you haven't put in any looping structions. Google "Excel VBA loop through cells" and you can find many examples to use.
5) I think the biggest issue that you are having is that you are overwhelmed because you are trying to do everything all at once. I would suggest solving one problem at a time and then putting the code together. For instance, you want to open two files. Start by writing code just to open one file. Then try to get a value out of the file into the open workbook. Then open another file. Then get data out of that file. Then test looping through cells in the current workbook and checking for desired criteria and pasting the results if they match. Then combine all of these things into coherent code.
Nobody writes efficient code the first time they try, so even if you end up with really long code that isn't efficient, you'll learn a lot and have something. Even experienced programmers can get stuck or write code that doesn't work the first or even tenth time. If you get stuck, ask a specific question about a specific error message or specific issue that you can't resolve.

VBA - getting column # with Find(), get error 91 - object var. or with block var. not set

I have a VBA script wherein I am trying to use find() to get the column number of a column on a separate worksheet (same workbook).
So, from "Sheet 1" I run this macro, to find the column on "Sheet 2" that has the word "Ins Val" in row 1. What's odd is that earlier in the macro, I use the same formula to get a column number without issue. However, the below code throws a "Run Time Error 91, Object Variable or With Block Not Set" but I can't figure why.
dim useDataWS as Worksheet, typeValColumn as Integer, theType as String, mainWS as Worksheet
Set mainWS = worksheets("Sheet 1")
Set useDataWS = worksheets("Sheet 2")
theType = mainWS.Cells(49,5).Value
'' the below line gives the error
typeValColumn = useDataWS.rows(1).Find(what:=theType, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False).Column
But, earlier in that macro, I do the same thing- with no error :?
With useDataWS
noOneCol = .Rows(1).Find(what:=theType, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False).Column
End With
Note: Even if I change that first part to use "with", the same error occurrs.
Any ideas? (Note: It's a more robust script, but I tried to get the parts that apply. If there's something else that might be causing this, let me know what other kinds of things I'm doing that could mess this up and I'll post more of the code).
edit: Thanks #Branislav Kollár - using search by "xlFormulas" instead of "xlValues" solves the issue. (Although I still don't know why Excel throws the error with 'values', but hey - it works!)
The reason, why the following line
typeValColumn = useDataWS.rows(1).Find(what:=theType, LookIn:=xlValues, lookat:=xlWhole, MatchCase:=False).Column
was giving an error is because the Find()method found Nothing, and applying .column to a Nothing results in error.
Solution can be replacing LookIn:=xlValues with LookIn:=xlFormulas.
I'd like to provide more info about why it does work, but I don't know honestly. One thing I found out is that LookIn:=xlFormulas will find even hidden cells and I guess it has more general usage.
More about Find() method can be found on MSDN Range.Find Method, or Find Method in Excel VBA or .Find and .FindNext in Excel VBA
Sometimes it is better to revert back to the native worksheet functions. While .Find is better for a worksheet-wide search, both .CountIf and .Match can easily locate a value in a single row or column. I typically use .CountIf to see if the value if there and .Match to retrieve the column index number.
with useDataWS
if cbool(application.countif(.rows(1), theType)) then
typeValColumn = application.match(theType, .rows(1), 0)
else
debug.print "not found in row 1"
end if
end if
You can also check for the value's existence with If IsError(app.Match(...)) but I prefer the two-function approach. If you can write a MATCH worksheet function that does not return #N/A then the above code will locate theType.
This is not so much an answer as a quick diagnostic tool. You swear that Ins Val is in row 1 of Sheet 2. Put this formula in the cell directly below the one containing Ins Val.
=CODE(MID(H$1, ROW(1:1), 1))
Adjust H$1 for the cell that actually contains Ins Val and fill down for at least 9 rows.
Excel 2013 addendum:
Excel 2013 introduced the UNICODE function that may be more appropriate for this purpose.
=UNICODE(MID(H$1, ROW(1:1), 1))
        
If you are receiving anything other than the results shown above, then you have rogue characters in the cell value.
The easiest one-off solution is to retype the value. If this is a repeated import then other methods need to be in place to compensate. The best method is to adjust the import at the source but that is not always possible. The worksheet function CLEAN does a reasonable job of removing rogue characters but it is not the best. Sometimes custom routines need to be written that address specific circumstances.

Searching a range of columns defined by variables in VBA

I'm trying to search a range of columns on a different worksheet, where the range is defined by two separate variables. I have successfully been able to use the same code to search a range of columns that I manually inputted, but using variables result in an error:
Run-time error '1004':
Application-defined or object-defined error
I am using the code to search a separate worksheet for the column number of the first instance of the month and then search a range beginning with that column number for the specific day.
An example of the worksheet I'm searching through:
http://i.imgur.com/ljmmGGi.png
Below is the code. Specifically, the MonthFind function has worked perfectly, but the subsequent DayFind function, which uses output from MonthFind is acting up.
Private Sub ComboBox21_Change()
Dim i As String
Dim j As String
i = "February"
j = 9
Dim MonthFind As Variant
With Sheets("Project Schedule").Range("A1:ZZ1")
Set MonthFind = .Find(i, LookAt:=xlWhole, MatchCase:=False)
End With
Dim Month1 As Integer
Dim Month2 As Integer
Month1 = MonthFind.Column
Month2 = MonthFind.Column + 12
Dim DayFind As Variant
With Sheets("Project Schedule").Range(Columns(Month1), Columns(Month2))
Set DayFind = .Find(j, LookAt:=xlWhole, MatchCase:=False)
End With
End Sub
Any help would be much appreciated, I've been trying so many different variations of this code to no avail!
Edit - Link to Excel file: https://www.dropbox.com/s/275fo0uucfeum3y/Project%20Scheduling%20SO.xlsm?dl=0
I almost gave up on this, but I found out what the problem was.
Your ComboBox_21 object has an input range (which will fill in the combobox with the selectable values) on the Inputs sheet that uses a bunch of formulas that reference the Project Schedule sheet. Whenever you do all those copy/paste functions against the range in Project Schedule that the combobox relies on, you are effectively changing the data for the dropdown box, and in turn, causing the _Change() event to fire off with every paste that effects that area.
This isn't always a problem (though in my opinion, it's causing a lot of unnecessary code execution), but there's this bit of code that is causing an issue in your AddJob1_Click() event:
Range(Sheet1.Cells(erow, 5), Sheet1.Cells(erow + 3, 10000)).ClearContents
Apparently you're not allowed to perform the Range.Find() method when the contents of the cells that the combobox rely on are being changed.
Here's some info about that, though it's not terribly helpful:
https://msdn.microsoft.com/en-us/library/office/aa221581(v=office.11).aspx
So that's the why, now how to fix it:
Defer automatic calculation until your UserForm code is finished doing whatever it needs to do.
This will ensure that you are allowed to do the Find's and referencing you need to do.
Private Sub AddJob1_Click()
' turn off automatic calculation
Application.Calculation = xlCalculationManual
erow = Sheet1.Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Row
' ... other stuff
' turn calculation back on and perform a calculate, which will fire off the ComboBox21_Change() event
Application.Calculation = xlCalculationAutomatic
Application.Calculate
Unload Me
End Sub

Run time error '1004' Unable to get the Match propertyof the WorksheetFunction class

In my macro, I have the following code :
i = Application.WorksheetFunction.Match(str_accrual, Range(Selection, Selection.End(xlToRight)), 0)
where 'str_accrual' is a string captured earlier to this line and the Range selected is in a single row say from "A1" to "BH1" and the result will be a number which is the position of that string in that range selected.
When I run the macro, I get the error:
Run time error '1004' Unable to get the Match propertyof the WorksheetFunction class
But when I run the macro line by line using (F8) key, I don't get this error but when I run the macro continuously I get the error. Again, if the abort the macro and run it again the error doesn't appear.
I tried several times. It seems that if there is no match, the expression will prompt this error
if you want to catch the error, use Application.Match instead
Then you can wrap it with isError
tons of posts on this error but no solution as far as I read the posts. It seems that for various worksheet functions to work, the worksheet must be active/visible. (That's at least my latest finding after my Match() was working randomly for spurious reasons.)
I hoped the mystery was solved, though activating worksheets for this kind of lookup action was a pain and costs a few CPU cycles.
So I played around with syntax variations and it turned out that the code started to work after I removed the underscore line breaks, regardless of the worksheet being displayed. <- well, for some reason I still had to activate the worksheet :-(
'does not work
'Set oCllHeader = ActiveWorkbook.Worksheets("Auswertung").Cells(oCllSpielID.Row, _
Application.Match( _
strValue, _
ActiveWorkbook.Worksheets("Auswertung").Range( _
oCllSpielID, _
ActiveWorkbook.Worksheets("Auswertung").Cells(oCllSpielID.Row, lastUsedCellInRow(oCllSpielID).Column)), _
0))
'does work (removed the line breaks with underscore for readibility) <- this syntax stopped working later, no way around activating the worksheet :-(
Set oCllHeader = ActiveWorkbook.Worksheets("Auswertung").Cells(oCllSpielID.Row, Application.Match(strValue, ActiveWorkbook.Worksheets("Auswertung").Range(oCllSpielID, ActiveWorkbook.Worksheets("Auswertung").Cells(oCllSpielID.Row, lastUsedCellInRow(oCllSpielID).Column)), 0))
In the end I am fretting running into more realizations of this mystery and spending lots of time again.
cheers
I was getting this error intermittently. Turns out, it happened when I had a different worksheet active.
As the docs for Range say,
When it's used without an object qualifier (an object to the left of the period), the Range property returns a range on the active sheet.
So, to fix the error you add a qualifier:
Sheet1.Range
I had this issue using a third-party generated xls file that the program was pulling from. When I changed the export from the third-party program to xls (data only) it resolved my issue. So for some of you, maybe there is an issue with pulling data from a cell that isn't just a clean value.
I apologize if my nomenclature isn't great, just a novice to this.
That is what you get if MATCH fails to find the value.
Try this instead:
If Not IsError(Application.Match(str_accrual, Range(Selection, Selection.End(xlToRight)), 0)) Then
i = Application.Match(str_accrual, Range(Selection, Selection.End(xlToRight)), 0)
Else
'do something if no match is found
End If
Update
Here is better code that does not rely on Selection except as a means of user-input for defining the range to be searched.
Sub Test()
Dim str_accrual As String
Dim rngToSearch As Range
str_accrual = InputBox("Search for?")
Set rngToSearch = Range(Selection, Selection.End(xlToRight))
If Not IsError(Application.Match(str_accrual, rngToSearch, 0)) Then
i = Application.Match(str_accrual, rngToSearch, 0)
MsgBox i
Else
MsgBox "no match is found in range(" & rngToSearch.Address & ")."
End If
End Sub
I used "If Not IsError" and the error kept showing. To prevent the error, add the following line as well:
On Local Error Resume Next
when nothing is found, Match returns data type Error, which is different from a number. You may want to try this.
dim result variant
result = Application.Match(....)
if Iserror(result)
then not found
else do your normal thing

Returning the Cell location of specific value in a separate Workbook

To clarify. I have 1 spreadsheet I am creating the VBA in. When the Macro runs, I want the code to find a specific value in a separate WorkBook sheet and return the location of the value in the other WorkBook.
My initial thought was to use HLookUp, however that will only return the value, not the location of the value. My next thought was to use the Find function, but I can't get it to run. The error I get is: Run-time error "438': Object doesn't support this property or method
startValue = enrollBook.Sheets("Pop-FY").Range("D:Z"). _
Applications.WorksheetFunction. _
Find(What:=FYString, LookIn:=xlValues)
enrollBook is the other workbook.
startValue is supposed to be the location of the found value in the other spreadsheet
This is a weird way of calling a function & formatting you've got there, I first even thought that code was invalid.
First, you are confusing Range.Find with Application.WorksheetFunction.Find. You need the Range one, but are calling the other one.
Second, the error is because it's Application, not Applications.
Third, you will need a Set:
Set startValue = enrollBook.Sheets("Pop-FY").Range("D:Z").Find(What:=FYString, LookIn:=xlValues)
You mean something like this?
Dim sTest As String
Dim oRange As Range
Set oRange = Worksheets(1).Range("A1:Z10000").Find("Test", lookat:=xlPart)
MsgBox oRange.Address
I tested this, but you need to change your parameters.