Why VBA sometimes reconized the next variable (x1Whole) and sometimes not - excel

I have a first version of the next code in VBA for excel
Function findCell(celda As String, rnc As String) As String
Dim cell As Range
Dim pos As String
Range("A2").Select
Set cell = Cells.Find(What:=celda, After:=ActiveCell, LookIn:= _
xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False)
If cell Is Nothing Then
pos = 0
Else
pos = cell.row
End If
findCell = pos
End Function
The function recive a string and return the position in number of the column, but After I change a parameter because I must be find the full contain of the cell. And I change the value lookAt from x1Part to x1Whole
Set cell = Cells.Find(What:=celda, After:=ActiveCell, LookIn:= _
xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False)
When I try to run the macro doesnt work sometimes and show me that value x1Whole, but when I run from the editor works.

If you seek a string in a formula as xlPart, you always get the string from your function itself (a good idea, btw). There will be a problem with xlWhole: there may be a case (and if the formula is the only thing in the sheet, it is is the case) that there's no match. Find will give an error, if nothing is found, and the formula result will be #N/A.
Below is your code with error handling, resulting in 0 for no match.
Function findCell(celda As String, rnc As String) As String
Dim cell As Range
Dim pos As String
On Error GoTo Nomatch
Set cell = Cells.Find(What:=celda, After:=Range("A2"), LookIn:= _
xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, SearchFormat:=False)
Nomatch:
If cell Is Nothing Then
pos = 0
Else
pos = cell.Row
End If
findCell = pos
End Function
Also, I removed Select("A2"), and moved it to After:=Range("A2").

Related

VBA .Find Changing Arguments Dynamically

I have a VBA function that uses the the .find method. In my function I'd like change the LookAt: argument between xlPart or xlWhole. I tried using a parameter variable matchvalue and passing in "xlPart" or "xlWhole". It didn't work.
Is there a way to change the LookAt: argument with a variable I pass into my function?
Public Function getfield(matchvalue as string)
GetRowNumber = Cells.Find(What:="Cat", After:=ActiveCell, LookIn:=xlValues, LookAt:= _
matchvalue, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
End Function
Not sure if the way you are splitting the lines makes you can't achieve your goal.
Try with:
Public Function getfield(matchvalue as string)
GetRowNumber = Cells.Find(What:="Cat", After:=ActiveCell, LookIn:=xlValues, _
LookAt:=matchvalue, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
End Function
Reading .Find documentation, LookAt value should be Variant Type.
Read the following link.
https://msdn.microsoft.com/en-us/vba/excel-vba/articles/range-find-method-excel
It's not clear what your function is intended to return, but here's how I'd probably handle this:
Public Function getfield(Optional matchWholeValue as Boolean = True)
GetRowNumber = Cells.Find(What:="Cat", After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:= IIf(matchWholeValue, xlWhole, xlPart), _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Row
End Function
To match on the whole value use:
getfield()
and to match on part:
getfield(False)
Try:
Public Function GetRowNumber(byval matchvalue as string, optional byval LookAtWhole = True) as long
Dim Result as range
Set Result =
Activesheet.Cells.Find(What:=matchvalue After:=ActiveCell, LookIn:=xlvalues, LookAt:=iif(LookAtWhole,xlwhole,xlpart), SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _ , SearchFormat:=False)
If not (Result is nothing) then
Getrownumber = result.row
Else
Getrownumber = 0
End if
End Function
This function performs FIND method on active sheet's cells. Returns 0, if the criteria returned no results -- else the row number. You could add further parameters.
Hopefully it works and is what you wanted?

Highlight cells in a column from active cell down to Variable row

I would like some help finishing of this piece of code. I need to highlight cells in a column from active cell down to Variable row.
Snippet of my code:
Start = Cells.Find(What:="MEETING", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
ActiveCell.Offset(7, 0).Select
Range(ActiveCell & Stop).Select 'it is this bit that doesn't work
You rarely have to activate or select a range
Sub BrokenPiece()
Dim rStart As Range
Dim rStop As Range
Dim CompleteSelection As Range
Set rStart = Cells.Find(What:="MEETING", After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False)
If Not rStart Is Nothing Then
Set rStop = rStart.Offset(7, 0)
Set CompleteSelection = Range(rStart, rStop)
CompleteSelection.Select
End If
End Sub
If you want to find the address of the last occurrence of Note: in Columns(1).
DateStop = Columns(1).Find(What:="Note:", LookAt:=xlPart, SearchDirection:=xlPrevious, MatchCase:=False).Row - 2
You would first set your range
DateStop = Columns(1).Find(What:="Note:", LookAt:=xlPart, SearchDirection:=xlPrevious, MatchCase:=False)
Then make sure that you actually found something
If not DateStop is nothing then
Now there are to ways to get the row that you want
result = DateStop.Row -2
or
result = DateStop.Offset(-2).Row
If you choose the Offset method, you should test that DateStop.Row -2 is a valid row. In this you will not throw an error
If DateStop.Row -2 > 0 then
So let's put it together
Function DateStopRowMinus2()
Dim DateStop
Set DateStop = Columns(1).Find(What:="Note:", LookAt:=xlPart, SearchDirection:=xlPrevious, MatchCase:=False)
If Not DateStop Is Nothing Then
If DateStop.Row - 2 > 0 Then
DateStopRowMinus2 = DateStop.Offset(-2)
End If
End If
End Function
Notice when I referred to the Offset of DateStop, I didn't specify a column. That is because the Offset property takes two optional parameters
Offset([Row], [Column])
We know that they are optional because the parameters are enclosed in square brackets []. Here are some valid examples
Range("A10").Offset(2)
Range("A10").Offset(2, 0)
Range("A10").Offset(0, 2)
Range("A10").Offset(0, 2)
Range("A10").Offset(,2)
Range("A10").Offset(2, 2)

Ms Excel Replace value with the average of the previous and next values

I'm working with hourly weather data in Excel that has each hour of every day of the year along with the corresponding temperature value that was recorded.
Some of the values weren't recorded, and instead show up as just an "M" on the spreadsheet. For example, A32 = 28, A33 = M, A34 = 30. I want to replace that "M" with a formula to take the average of the previous and next values. I know how to do this manually, but I am having difficulty writing a Macro to find all the M's in the spreadsheet, then auto-replace it as stated above.
My main obstacle is getting excel to use the correct values when replacing the "M".
Here is my code
Sub MReplace()
'
' MReplace Macro
'
'
ActiveCell.Select
Cells.Find(What:="M", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
Cells.FindNext(After:=ActiveCell).Activate
ActiveCell.Offset(-8, 1).Range("A1").Select
Cells.Find(What:="M", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
, SearchFormat:=False).Activate
Cells.FindNext(After:=ActiveCell).Activate
ActiveCell.Replace What:="M", Replacement:="[****This is what I am having difficulty with****]", LookAt:=xlWhole, _
SearchOrder:=xlByRows, MatchCase:=True, SearchFormat:=False, _
ReplaceFormat:=False
Cells.Find(What:="M", After:=ActiveCell, LookIn:=xlFormulas, LookAt:= _
xlWhole, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=True _
, SearchFormat:=False).Activate
End Sub
I have heard of something that you can put in to the code that can address the selected cell. I think it's cell() but I am not sure. Maybe that is a way to get it to work better?
Try this code:
Sub MReplace()
Dim ws As Worksheet
Dim cel As Range
Dim firstAddress As String
Set ws = ActiveSheet
Set cel = ws.Range("A:A").Find("M")
If Not cel Is Nothing Then
firstAddress = cel.Address
Do
cel.Value = (cel.Offset(1) + cel.Offset( -1)) / 2
Set cel = ws.Range("A:A").FindNext(cel)
hr = False
If Not cel Is Nothing Then
If cel.Address <> firstAddress Then
hr = True
End If
End If
Loop While hr
End If
End Sub
It loops through all the cells containing "M" and replaces it with the average of the one on the right and the one on the left. It will error on any that are in the first column as there is no column to the left.

Selecting the second result of a "Find" with VBA

I am trying to make it so that I can find the second result for "lights", in case of having various occurrences for this term. The code below finds the first occurrence in the range under consideration.
Dim ws As Worksheet
Dim rng1 As Range
Dim y As Range
Columns("B:B").Select
Selection.Find(What:="1", After:=ActiveCell, LookIn:=xlValues, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Select
Set x = Range(Selection, Selection.End(xlDown)).Offset(0, 3)
Range(x.Address(0, 0)).Select
Selection.Find(What:="Lights", After:=ActiveCell, LookIn:=xlValues, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Select
FindNext delivers what you want. Using it is easy: perform the first search as you are doing it right now (although by assigning the result to a Range) and take the resulting range as starting point for FindNext. Here you have a sample code adapted to your specific requirements (secondAddress is the Address of the second occurrence of "Light", if any):
Dim foundRange As Range
Dim rangeToSearch As Range
Set rangeToSearch = Selection
Set foundRange = rangeToSearch.Find(What:="Lights", After:=ActiveCell, LookIn:=xlValues, LookAt:= _
xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False _
, SearchFormat:=False) 'First Occurrence
Dim secondAddress As String
If (Not foundRange Is Nothing) Then
foundRange.Activate
Dim count As Integer: count = 0
Dim targetOccurrence As Integer: targetOccurrence = 2
Dim found As Boolean
Do While Not found
Set foundRange = rangeToSearch.FindNext(foundRange)
If Not foundRange Is Nothing Then
count = count + 1
If (count >= targetOccurrence - 1) Then
secondAddress = foundRange.Address
Exit Do
End If
Else
Exit Do
End If
Loop
End If
I found an even easier way as it sounds like I had a similar problem.
If you simplified your search function:
Cells.Find(What:="xxxx", After:=Cells(1, 1), LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Select
Then add another line beneath:
Cells.Find(What:="xxxx", After:=ActiveCell, _
LookIn:=xlValues, LookAt:=xlPart, SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Select
All this does is find the first occurrence of "xxxx", then the second code finds "xxxx", but begins searching from the result of the first find code (which was the ActiveCell).

Excel Input Box VBA Error Checking Problem

Below is the code that I changed. I cannot figure out VBA for the life of me. If this was c++ it would have taken me 30 seconds to write. I am still getting the errors.
Sub CodeFinder()
Dim userInput As String
Dim errorCheck As String
userInput = InputBox("Please enter the code to search", "Code Search Engine")
errorCheck = Cells.Find(What:=userInput, _
After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False)
If errorCheck = False Then
MsgBox ("Error")
Else
Cells.Find(What:=userInput, _
After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False).Activate
End If
End Sub
If Cells.Find fails it returns Nothing. So you need to assign it to a variable, and check its value before trying to .Activate it.
In fact you should also check the return value of InputBox in case Cancel was clicked.
EDIT: Still contains a number of errors.
Cells.Find returns a Range, but you are trying to assign it to a String variable. (Also don't forget that Range and String variables have different assignment statements.)
You then try to compare the variable to False instead of checking that it isn't Nothing.
You then need to activate the found Range rather than trying to find it again.
Sub CodeFinder()
Dim userInput As String
Dim rFound As Range
userInput = InputBox("Please enter the code to search", "Code Search Engine")
If Len(userInput) > 0 Then
Set rFound = ActiveSheet.Cells.Find(What:=userInput, _
After:=ActiveCell, LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False)
If Not rFound Is Nothing Then
rFound.Select
Else
MsgBox "No cells found"
End If
End If
End Sub

Resources