I'm looking to create an IF/Else statment in VBA that when run will return a True or False value based on if a cell contains a certain string of text. So basically I want a True value returned if the cell contains the specified string or a False value if it does not contain the specified string. This formula needs to run down a range of cells not just one if that makes a difference. I'm thinking I need to use the InStr function, but I'm not 100% sure if that is correct or how to implement it. I am fairly new to VBA so any help I can get would be greatly appreciated as I have been stuck on this for several hours.
Thank You
Well this is a very badly asked question but I think everyone has needed some help at some point in life. That being said, here is the code you are asking for
Sub FindString()
'Declare the range
Dim rng As Range
'Assign the range to find
Set rng = ActiveSheet.Range("A1:A100")
'Loop though each cell
For Each cell In rng.Cells
'Check if cell has the string and set text
'of the next column to True or False
cell.Offset(0, 1).Value = IIf(InStr(1, cell, "stringToFind"), "True", "False")
Next
End Sub
Give it a try and let me know your comments
Why use VBA? An expression in cell can return True/False.
=ISNUMBER(FIND("something",A1))
I tested #3vts code and it runs but the output is wrong. This version works:
Sub FindString()
Dim rng As Range
Set rng = ActiveSheet.Range("A1:A100")
For Each cell In rng.Cells
If InStr(1, cell, "text to find") > 0 Then
cell.Offset(0, 1).Value = True
Else
cell.Offset(0, 1).Value = False
End If
Next
End Sub
I certainly hope you learned something in spite of the work was done for you.
May be the below one can help you, It gives you whether the excel contains the Word.
Dim ra As Range
Set ra = Cells.Find(What:=Word, LookIn:=xlFormulas, LookAt _
:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False)
If ra Is Nothing Then
'False (Not Found)
Else
'True (Found)
End If
Related
I am trying to search a specific column in Excel (Column K) using the below VBA code but when I run the macro it instead searches the whole sheet instead of the specified column.
The problem is it firstly finds 'mycell1' in an earlier column, i.e. in Column C instead of Column K which I don't want it to do.
I have also tried using 'xlByRows' in the 'Searchorder' which had the same issue.
Would greatly appreciate any help please
Thanks
Range("K:K").Select
Set foundcell1a = Selection.Cells.Find(What:=mycell1, After:=ActiveCell, LookIn:=xlValues, LookAt _
:=xlWhole, SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:= _
False, SearchFormat:=False)
Set foundcell1a = Cells.FindNext
If Not foundcell1a Is Nothing Then
foundcell1a.Activate
End If
Don't use .Select if you can possibly avoid it and most of the time, you can.
Try using With...End With constructs instead. Be as specific as you can with the object you want to operate on.
Sub SearchK()
Dim mycell1
Dim foundcell1a As Range
mycell1 = 1
With ActiveWorkbook.Worksheets("Sheet1").Range("K:K")
Set foundcell1a = .Find(mycell1, .Cells(.Rows.Count, 1))
Set foundcell1a = .FindNext(foundcell1a)
If Not foundcell1a Is Nothing Then
foundcell1a.Activate
End If
End With
End Sub
Without a With...End With, you would have to repeat all the object identifiers so:
Set foundcell1a = .Find(mycell1, .Cells(.Rows.Count, 1))
Would have to be expressed as:
Set foundcell1a = ActiveWorkbook.Worksheets("Sheet1").Range("K:K").Find(mycell1, .Cells(.Rows.Count, 1))
When VBA is evaluating a command it needs to evaluate each property preceding a period (.) every time it encounters it. Using ActiveWorkbook.Worksheets("Sheet1").Range("K:K") gets rid of 4 periods so it runs faster too.
The Set foundcell1a = .Find(mycell1, .Cells(.Rows.Count, 1)) is saying find mycell1 after the last used cell in column K so it loops back to find the first instance in column K regardless of the active cell.
Try this:
With Range("K:K")
Set LastCell = .Cells(.Cells.Count)
End With
Set FoundCell1a = Range("K:K").Find(mycell1, LastCell)
If Not FoundCell1a Is Nothing Then
FirstAddr = FoundCell1a.Address
Range(FirstAddr).Activate
End If
Hope this helps!
I am looping through a column with ~5000 rows looking for a specific unique record. Once found, I offset and replace it with a value from a range. This is naturally rather resource intensive and I found it to occasionally freeze older machines running the macro.
My idea is now to replace this with a Search & Replace macro but am wondering if the performance is actually faster since the process of checking each cell in range for a value would still be the same?
Here the code I have so far. How would a Search & Replace look like and is worth it?
Sub Replace_List()
Dim rList As Range, cel As Range, n As Long
Dim fnd As Range
Dim fndFirst As String
Application.ScreenUpdating = False
With ThisWorkbook.Sheets("Settings")
Set rList = .Range("D4", .Range("D" & .Rows.Count).End(xlUp))
End With
For Each cel In rList
Set fnd = ThisWorkbook.Worksheets("Data").Columns("A:A").Find(What:=cel.Value, LookAt:=xlWhole)
If Not fnd Is Nothing Then
fndFirst = fnd.Address
Do
fnd.Offset(0, 1).Value = cel.Offset(0, 2).Value
Set fnd = ThisWorkbook.Worksheets("Data").Columns("A:A").FindNext(After:=fnd)
Loop While fnd.Address <> fndFirst
End If
Next
Application.ScreenUpdating = True
MsgBox "Replaced all items from the list.", vbInformation, "Replacements Complete"
End Sub
Note: This is not an answer, but rather something to ponder, and takes up way too much room for a comment.
This is pretty much the format I use on a "Search and Replace". I'm not sure if this ends up being any faster or not, but as you can see, it's not too different from your "find" statement. For me it works fine, but I'm not looking at thousands of records. Hope this helps you make a decision.
ThisWorkbook.Worksheets("Data").Columns("A:A").Replace What:=myValue1, Replacement:=myValue2, LookAt:=xlWhole, SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False, ReplaceFormat:=False
This question already has answers here:
How do I get the old value of a changed cell in Excel VBA?
(18 answers)
Closed 5 years ago.
I have the script below that pretty much does what I want, but I need it to fire off ONLY if someone deletes a value from the Range E9:E17. It is not really a SelectionEvent, it is more of a Change_Event, but if the change is the delete, the value is gone from the cell before I can capture it. I think the SelectionEvent has to call the change event. Does it make sense? Let me know if you need clarification. Thanks!!
Private Sub Worksheet_SelectionChange(ByVal Target As Excel.Range)
Application.EnableEvents = True
If Not Intersect(Target, Range("E9:E17")) Is Nothing Then
GetValue = ActiveCell.Value
GetCustomer = ActiveCell.Offset(0, -1).Value
With Sheets("LargeCustomerOP").Range("D2:D6") 'searches range in Col D
Set Rng = .Find(What:=GetCustomer, _
After:=.Cells(.Cells.Count), _
LookIn:=xlValues, _
LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, _
MatchCase:=False)
If Not Rng Is Nothing Then
Rng.Offset(0, 1).Value = Rng.Offset(0, 1).Value + GetValue
Else
'value not found
End If
End With
End If
Application.EnableEvents = True
End Sub
Save the data in the Worksheet_SelectionChange-Event in a global var and access it in the Worksheet_Change-Event. See this example on StackOverflow
Sorry, don't have time to write the code but I'm sure someone else can help there..
Create a mirror copy of the sheet on a hidden sheet by using Worksheet_Change. you only need to mirror the range you're worried about.
Each time a change is made perform the following in the event:
Check if the Target becomes blank. If it does, store the value of the same cell from the mirror.
Copy the value of the Target cell to the same cell on the mirror.
If the store has a value, then act upon it as you see fit.
I've been stuck trying to figure out what to do with this, but basically I want a way to print out the value in column B given a specific value that matches column A. So for example:
Column A Column B
1 ABC
2 DEF
3 GHI
1 JKL
I want to, after using find/findnext or whatever it is, to print out this string:
ABC JKL
I tried using
Set cellFound = ActiveWorkbook.Worksheets("sheet1").Range("F1:F1000000").Find("1")
string = cellFound.Offset(0, 1).value
And I have a loop to loop through as many time as it needs to get all the rows taken care of. But with find it keeps returning me the same first string ("ABC") and the string ends up being ABC ABC instead of ABC JKL
I tried using FindNext instead of find, but what I got is a 1004 Error. So I'm not really sure where I'm doing this wrong. Anyone has any idea?
You don't need FindNext if you start each Find after the previous one:
Sub qwerty()
Dim rFirst As Range, r As Range
Dim A As Range
Set A = Range("A:A")
Do
If rFirst Is Nothing Then
Set rFirst = A.Find(What:=1, After:=A(1))
Set r = rFirst
Else
Set r = A.Find(What:=1, After:=r)
If r.Address = rFirst.Address Then Exit Do
End If
MyString = MyString & " " & r.Offset(0, 1)
Loop
MsgBox MyString
End Sub
You need to call Find once, and then successively FindNext. But there are a couple of non-obvious things:
Each time you call FindNext, the search will start again from the upper-left corner of the range; unless you pass in the current found cell.
The search will wrap around (up or down, depending on your search direction. You need to write code that handles this possibility.
The minimal working code would look something like this:
Dim rng As Excel.Range
Set rng = ActiveWorkbook.Worksheets("sheet1").Range("F1:F1000000")
Dim lastRow as Integer
Set cellFound = rng.Find("1")
Do While Not cellFound Is Nothing
' handles wraparound
If cellFound.Row < lastRow Then Exit Do
string = cellFound.Offset(0, 1).Value
' do something here with string
Set cellFound = rng.FindNext(cellFound)
Loop
Reference:
Find method
FindNext method
When using the Range.FindNext method, one need just include some reference to the initial find position. For example, I recorded this macro using excel; while I'm not a fan of using selection and activate, I think it helps to understand how the method functions:
Sub Using_Find()
Selection.Find(What:="my search string here", After:=ActiveCell _
, LookIn:=xlFormulas, LookAt:=xlPart, SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
Selection.FindNext(After:=ActiveCell).Activate
End Sub
To generate this subroutine, I used the record > macro in excel, then selected Home > Find & Select > Find.
The way I see this subroutine working is:
Step #1: Find the first location of the string, activate it;
Step #2: FindNext looks after the active cell that we just activated, finds the next location of the string, then activates it;
Etc. etc. So, the observation here is that the .FindNext method needs some reference to the prior find cell (which the first answer accomplishes by manually identifying it as a unique reference). I'm not saying anything to that answer, it works just as well. My goal was to help provide some insight into the Range.FindNext method.
Some other points worth mentioning:
Range.FindNext will return a Range object. (Microsoft)
The After parameter is described as:
"The cell after which you want to search. This corresponds to the position of the active cell when a search is done from the user interface. Be aware that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell is not searched until the method wraps back around to this cell. If this argument is not specified, the search starts after the cell in the upper-left corner of the range." (Microsoft)
...and
Under the Remarks section, Microsoft notes that, "The search will wrap around to the beginning of the range." They suggest to save the first address and do a check against it for each subsequent .FindNext. This way, once the method does wrap around, it will check the address against the first and end the check.
So, modeling the Range.FindNext Method provided by Microsoft, I wrote this introductory subroutine for review:
Sub USING_FIND()
'this line sets the range to our used range on the active sheet
With ActiveSheet.UsedRange
'setting c variable to .Find method, where the first value is what we're looking for,
'i.e. "1"; LookIn:= can be changed to our needs but set currently to xlValues
Set c = .Find(1, LookIn:=xlValues)
'begin first conditional; this conditional checks c (our .Find method) to see if it has
'some reference, then sets the address to a constant 'firstAddress' so we can check it
'against the .FindNext returns later to prevent endless loop
If Not c Is Nothing Then
firstAddress = c.Address
'Do...is where we place our "work"; this can be a redirect to another function/sub, etc
'for now I've just tossed a msgbox as a placeholder that returns the offset 1 column over
Do
MsgBox c.Offset(0, 1)
'Now we set c to the .FindNext method, using the original .Find method as the 'after'
Set c = .FindNext(c)
'Another empty reference check/exit as a conditional
If c Is Nothing Then
GoTo DoneFinding
'ends the empty reference conditional
End If
'using our .FindNext method that we replaced 'c' with earlier, we can now loop through
'the remainder of the value returns. The Loop While 'c.Address <> firstAddress' sentence
'is checking that each subsequent .FindNext address IS NOT the first address;
'-our loop will return to the 'Do' sentence to repeat the loop, starting on the
'MsgBox c.Offset(0,1) sentence with the next string occurence
'-the characters '<>' means 'does not equal'; i.e. the opposite of '='
Loop While c.Address <> firstAddress
'this ends the address check loop
End If
DoneFinding:
End With
End Sub
To adjust this code to your specific needs, we can change the sentence after the Do line: 'MsgBox c.Offset(0,1)' to our specific needs.
Depending on how complex your output needs are, you can add all occurrences to an array, then have the array output the values in order of how you want to see them. This can be done by redim array and preserve each return. Once the .Find loop completes, open a new workbook with the Workbooks.Open method, and run a quick loop that takes each array value and places it in the order that you prefer.
Another option is to 'print' to .txt. Open a new .txt as #1, then 'print' accordingly. This can also be done as a second subroutine via the array option suggested previously.
Hope this helps add some context to your initial question with respect to the .FindNext method, as well as provides some ideas for future direction/implementation. Good luck!
Microsoft page on Range.FindNext Method:
https://msdn.microsoft.com/en-us/VBA/Excel-VBA/articles/range-findnext-method-excel
Function FindMultiResut(ByRef What As String, _
ByRef FindRng As Range, _
ByRef OutputRng As Range, _
ByRef Delimite As String)
Dim fRng As Range
Dim Rng1 As Range
Dim Rng2 As Range
Dim temp As String
Set fRng = FindRng
Do
If Rng1 Is Nothing Then
Set Rng1 = fRng.Find(What:=What, After:=fRng(1))
Set Rng2 = Rng1
Else
Set Rng2 = fRng.Find(What:=What, After:=Rng2)
If Rng2.Address = Rng1.Address Then Exit Do
End If
If OutputRng.Worksheet.Cells(Rng2.Row, OutputRng.Column) <> Empty Then
temp = temp & OutputRng.Worksheet.Cells(Rng2.Row, OutputRng.Column) & Delimite
End If
Loop
FindMultiResut = Left(temp, Len(temp) - 1)
End Function
Here is an implementation of the suggestion I made in my comment under your question.
Function RowBeforeLast(ByVal What As Variant) As Long
Dim Fnd As Range
Set Fnd = Range("E:E").Find(What:=What, After:=Range("E1"), _
LookAt:=xlWhole, _
Searchdirection:=xlPrevious)
If Not Fnd Is Nothing Then
Set Fnd = Range("E:E").Find(What:=What, After:=Fnd, _
LookAt:=xlWhole, _
Searchdirection:=xlPrevious)
If Not Fnd Is Nothing Then RowBeforeLast = Fnd.Row
End If
End Function
It's designed as a UDF so that you can call it from the worksheet with a worksheet function like =RowBeforeLast(E5). You can also call it with code like
Private Sub TestGet()
RowBeforeLast "GR 3"
End Sub
Either way it will return the row number in which the search criterium was found for the second time from the bottom of the column. If there is only one or no occurrance the function will return zero.
I have requirement to write some function which will accept Range as input and I need to return value of first non empty cell. I have tried in one excel sheet and finding non empty cell was working fine. When I try with my project excel file it was not working. Basically for Find method of Range I am getting runtime error 13. Check below code and let me know what is the issue. I have noticed even in when I put Range.Row property it make "Row" as row in code ( in below code see Target.row).
Sub Btn_GenerateChartClicked()
If Range("E9") = "Scatter" Then
MsgBox "Scatter is selected"
Dim str As String
Dim rng As Range
Set rng = Range("B12:I12")
str = FindNonEmptyCellFromRange(rng)
' MsgBox str
Else
MsgBox "Bar is selected"
End If
End Sub
Function FindNonEmptyCellFromRange(Target As Range) As String
Dim ws As Worksheet
Set ws = Sheets("Benchmarking_Project")
Dim foundRange As Range
Set foundRange = Target.Find("*", Cells(Target.row, 1), xlFormulas, , xlByColumns, xlPrevious)
'Dim cellValue As String
'cellValue = foundRange.Value
FindNonEmptyCellFromRange = "Test"
'cellValue
End Function
You can't find a target.
use Cell.Find and then once you have the cell selected use Target.Address to get the address of the cell
So your CellValue would become:
CellValue = FoundRange.Address
Although, your question is a little vague as your not doing anything practicle with this UDF anyway
Your question does not provide enough details and the function call does not return the non empty cell. Whatever happens your function will return only Test.
Anyway when going through the code, your range has a single row in it.
Issue seems to be with the following code
Set foundRange = Target.Find("*", Cells(Target.row, 1), xlFormulas, , xlByColumns, xlPrevious)
There is no need to specify the After Parameter Cells(Target.row, 1)
After parameters corresponds to the position of the active cell when a search is done from the user interface. Notice that After must be a single cell in the range. Remember that the search begins after this cell; the specified cell isn’t searched until the method wraps back around to this cell. If you do no specify this argument, the search starts after the cell in the upper-left corner of the range.
Try to change that code to
Set foundRange = Target.Find("*", , xlFormulas, , xlByColumns, xlPrevious)
The following code may work for you
Sub Btn_GenerateChartClicked()
If Range("E9") = "Scatter" Then
MsgBox "Scatter is selected"
Dim str As String
Dim rng As Range
Set rng = Range("B12:I12")
str = GetFirstNonEmptyCell(rng)
' MsgBox str
Else
MsgBox "Bar is selected"
End If
End Sub
Public Function GetFirstNonEmptyCell(Target As Range)
Dim startCell As Range, firstNonEmptyCell As Range
For Each c In Target.Cells
If Trim(c.Value) <> "" Then
Found_Address = c.Address
Exit For
End If
Next
GetFirstNonEmptyCell = Found_Address
End Function
Ian your suggestion about not to use Cells(Target.Row,1) in Find method is right. I got my mistake. In that I have put column index as 1 but it should be 2 because my selected range is from Column B which means column index 2. So I got actually error because there is no column index 1 in that range. So if I put 2 instead of 1 in above mentioned call then it is working fine. Yes your right that I was not returning actually value of last non empty cell as that was my R&D code I kept changing it. So while posting it I forgot to change it. Thank you all for your reply