Find a string within a cell using VBA - string

I have been driving myself mad with this for a day, searched high and low, and am probably trying to be too cute so am totally stuck.
I am trying to run a simple if then
If a cell contains "%" I'd like it to do one thing, and if not another. For reasons I don't understand I can't get it to work out. I've clearly taken a couple ideas from elsewhere but still can't get it to work.
Complicating factors- I don't want to run this on the whole column, just a table, so it is embedded in a larger sub using lots or relative ActiveCells. I never know where in the A column I am going to run into the "% Change" so the Range always has to be variable. I want VBA/VBE to do something different when it comes upon a cell with the "%" in it. SO
Here is what the raw data looks like
Initial Value (6/30/06)
Value (12/31/06)
Net Additions (9/30/07)
Withdrawal (12/07)
Value (12/31/07)
Withdrawal (2008)
Value (12/31/08)
Addition (8/26/09)
Value (12/31/09)
Value (12/31/10)
Value (12/30/11)
Value (3/31/12)
% Change 1st Quarter
% Change Since Inception
But when I run the following it gets stuck in a bad loop where it should have pulled out into the "If Then" as opposed to the "Else" part of the sub.
Sub IfTest()
'This should split the information in a table up into cells
Dim Splitter() As String
Dim LenValue As Integer 'Gives the number of characters in date string
Dim LeftValue As Integer 'One less than the LenValue to drop the ")"
Dim rng As Range, cell As Range
Set rng = ActiveCell
Do While ActiveCell.Value <> Empty
If InStr(rng, "%") = True Then
ActiveCell.Offset(0, 0).Select
Splitter = Split(ActiveCell.Value, "% Change")
ActiveCell.Offset(0, 10).Select
ActiveCell.Value = Splitter(1)
ActiveCell.Offset(0, -1).Select
ActiveCell.Value = "% Change"
ActiveCell.Offset(1, -9).Select
Else
ActiveCell.Offset(0, 0).Select
Splitter = Split(ActiveCell.Value, "(")
ActiveCell.Offset(0, 9).Select
ActiveCell.Value = Splitter(0)
ActiveCell.Offset(0, 1).Select
LenValue = Len(Splitter(1))
LeftValue = LenValue - 1
ActiveCell.Value = Left(Splitter(1), LeftValue)
ActiveCell.Offset(1, -10).Select
End If
Loop
End Sub
All help is appreciated, thank you!

I simplified your code to isolate the test for "%" being in the cell. Once you get that to work, you can add in the rest of your code.
Try this:
Option Explicit
Sub DoIHavePercentSymbol()
Dim rng As Range
Set rng = ActiveCell
Do While rng.Value <> Empty
If InStr(rng.Value, "%") = 0 Then
MsgBox "I know nothing about percentages!"
Set rng = rng.Offset(1)
rng.Select
Else
MsgBox "I contain a % symbol!"
Set rng = rng.Offset(1)
rng.Select
End If
Loop
End Sub
InStr will return the number of times your search text appears in the string. I changed your if test to check for no matches first.
The message boxes and the .Selects are there simply for you to see what is happening while you are stepping through the code. Take them out once you get it working.

you never change the value of rng so it always points to the initial cell
copy the Set rng = rng.Offset(1, 0) to a new line before loop
also, your InStr test will always fail
True is -1, but the return from InStr will be greater than 0 when the string is found. change the test to remove = True
new code:
Sub IfTest()
'This should split the information in a table up into cells
Dim Splitter() As String
Dim LenValue As Integer 'Gives the number of characters in date string
Dim LeftValue As Integer 'One less than the LenValue to drop the ")"
Dim rng As Range, cell As Range
Set rng = ActiveCell
Do While ActiveCell.Value <> Empty
If InStr(rng, "%") Then
ActiveCell.Offset(0, 0).Select
Splitter = Split(ActiveCell.Value, "% Change")
ActiveCell.Offset(0, 10).Select
ActiveCell.Value = Splitter(1)
ActiveCell.Offset(0, -1).Select
ActiveCell.Value = "% Change"
ActiveCell.Offset(1, -9).Select
Else
ActiveCell.Offset(0, 0).Select
Splitter = Split(ActiveCell.Value, "(")
ActiveCell.Offset(0, 9).Select
ActiveCell.Value = Splitter(0)
ActiveCell.Offset(0, 1).Select
LenValue = Len(Splitter(1))
LeftValue = LenValue - 1
ActiveCell.Value = Left(Splitter(1), LeftValue)
ActiveCell.Offset(1, -10).Select
End If
Set rng = rng.Offset(1, 0)
Loop
End Sub

For a search routine you should look to use Find, AutoFilter or variant array approaches. Range loops are nomally too slow, worse again if they use Select
The code below will look for the strText variable in a user selected range, it then adds any matches to a range variable rng2 which you can then further process
Option Explicit
Const strText As String = "%"
Sub ColSearch_DelRows()
Dim rng1 As Range
Dim rng2 As Range
Dim rng3 As Range
Dim cel1 As Range
Dim cel2 As Range
Dim strFirstAddress As String
Dim lAppCalc As Long
'Get working range from user
On Error Resume Next
Set rng1 = Application.InputBox("Please select range to search for " & strText, "User range selection", Selection.Address(0, 0), , , , , 8)
On Error GoTo 0
If rng1 Is Nothing Then Exit Sub
With Application
lAppCalc = .Calculation
.ScreenUpdating = False
.Calculation = xlCalculationManual
End With
Set cel1 = rng1.Find(strText, , xlValues, xlPart, xlByRows, , False)
'A range variable - rng2 - is used to store the range of cells that contain the string being searched for
If Not cel1 Is Nothing Then
Set rng2 = cel1
strFirstAddress = cel1.Address
Do
Set cel1 = rng1.FindNext(cel1)
Set rng2 = Union(rng2, cel1)
Loop While strFirstAddress <> cel1.Address
End If
If Not rng2 Is Nothing Then
For Each cel2 In rng2
Debug.Print cel2.Address & " contained " & strText
Next
Else
MsgBox "No " & strText
End If
With Application
.ScreenUpdating = True
.Calculation = lAppCalc
End With
End Sub

Related

FormulaR1C1 not running across a selected range

I'm trying to make a macro where I can find a set of products and get an average weekly sales figure. However, FormulaR1C1 is only applying to the first figure in a selection and not all active cells. Any help as to why it doesn't apply for the whole range?
Option Compare Text
Private Sub submit_Click()
Dim emptyRow As Long
Dim Rng As Range
Dim myCell As Object
Dim mySelection As Range
Range("R1:S1").Select
Range("R1:S1").Clear
ActiveCell.FormulaR1C1 = ""
emptyRow = WorksheetFunction.CountA(Range("R:R")) + 1
Cells(emptyRow, 18).Value = search.Value
Cells(emptyRow, 19).Value = week.Value
Set Rng = Range("A1:A2000")
searchString = Range("R1")
For Each myCell In Rng
If InStr(myCell.Text, searchString) Then
If Not mySelection Is Nothing Then
Set mySelection = Union(mySelection, myCell)
Else
Set mySelection = myCell
End If
End If
Next
If mySelection Is Nothing Then
MsgBox "The product was not found in the selection"
Else
mySelection.Offset(0, 4).Select
End If
ActiveCell.FormulaR1C1 = "=RC[-2] /" & Range("S1")
Unload Me
End Sub
The selection may contain more than one cell, but only one is the active cell.
If mySelection Is Nothing Then
MsgBox "The product was not found in the selection"
Else
mySelection.Offset(, 4).FormulaR1C1 = "=RC[-2] /" & Range("S1")
End If

Highlight all words in a long text that is in a Cell

I am trying to develop a Find button, to mark in red "ALL" of the word that are contained in a cell.
For example If I have in my cell this text.
"Pepper had peppermint in his pocket"
it should change to this.
"Pepper had peppermint in his pocket"
This code highlights the first word that it finds.
Dim i As Long
Dim oldrngrow As Long
Dim myValue As String
Dim arr() As Variant
arr = Array(TextBox1.Value)
TextBox2.Text = UBound(arr)
For i = 1 To UBound(arr) + 1
myValue = arr(i - 1)
If myValue = vbNullString Then
MsgBox ("Please Enter a Word in Textbox")
End
End If
Set rng = Cells.Find(What:=myValue, After:=Cells(1, i), LookIn:= _
xlValues, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:= _
xlNext, MatchCase:=False, MatchByte:=True, SearchFormat:=False)
If rng Is Nothing Then
GoTo skip
End If
oldrngrow = rng.Row
Do While rng.Column = i
If ComboBox1.Text = "Red" Then
rng.Characters(InStr(rng, myValue), Len(myValue)).Font.ColorIndex = 3
Set rng = Cells.FindNext(After:=rng)
If oldrngrow = rng.Row Then
Exit Do
End If
Loop
skip:
Next i
Interesting question. After some research, I’ve put together the following code to demonstrate how to highlight every instance of a word in a string within a cell. For the sake of the demonstration, it uses an Input Box to get the desired string-to-highlight (you can change the method), and assumes the range to search is simply A1 – again you can change this to whatever you want.
Make sure you include Option Compare Text at the top of the Sub – otherwise the search will be case sensitive. Let me know how you go.
Option Compare Text
Sub StringColor()
Dim myRange As Range, myCell As Range, myString As String, myCount As Integer
Set myRange = Range("A1")
myString = InputBox("Type the word you want to color in A1")
For Each myCell In myRange
For myCount = 1 To Len(myCell) - Len(myString) + 1
If Mid(myCell, myCount, Len(myString)) = myString Then
myCell.Characters(myCount, Len(myString)).Font.Color = vbRed
End If
Next myCount
Next myCell
End Sub

Find list of words from a range if words exits multiple times

I have a list of words in Sheet1 I need to match one by one from Sheets("Sheet2").Range("A1:A7500") until the end of the Range. Whenever word is matched I need to do something with it in Sheet1. That word occurs multiple times in Sheets("Sheet2").Range("A1:A7500").
Following code is Finding word only once. I dont understand where it is going wrong.
Sub XMAX()
Dim lrow As Long
Dim cel As Range
Dim oRng As Range: Set oRng = Sheets("Sheet2").Range("A1:A7500")
Dim oFoundRng As Range, oLastRng As Range
lrow = Sheets("sheet1").Cells(Sheets("Sheet1").Rows.Count, "f").End(xlUp).Row
'''''''''''''''Sheet1'''''''''''''''
For Each cel In Range("f4:f" & lrow)
If IsEmpty(cel.Value) = False Then
Set oFoundRng = oRng.find(cel.Value)
Do While Not oFoundRng Is Nothing
If UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("X" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "YO" Then
Range("V" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "JAN" Then
Range("U" & cel.Row).Value = "X"
Else
MsgBox oFoundRng.Value
End If
Set oLastRng = oFoundRng
Set oFoundRng = oRng.FindNext(cel.Value) 'Getting Error(1004) here "unable to get findnext property of the range class"
If oLastRng >= oFoundRng Then
Exit Do
End If
Loop
Else
End If
Next
Change this line
Set oFoundRng = oRng.FindNext(oFoundRng)
to
Set oFoundRng = oRng.FindNext
You are not searching for the word but for the range you previously found. You actually don't need to pass a value to .FindNext at all.
You also have to change this line
If oLastRng >= oFoundRng Then
to
If oLastRng.Row >= oFoundRng.Row Then
since the first line compares the values (which is not what you want to do since it will always evaluate to True). You actually want to compare the row numbers.
On another note, the following code snippet does not work:
If UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("X" & cel.Row).Value = "X"
ElseIf UCase(oFoundRng.Offset(0, 1).Value) = "ISAAC" Then
Range("W" & cel.Row).Value = "X"
This ElseIf will never be triggered since the condition is the same as the initial If condition.
You also don't need both of these statements:
Set oFoundRng = Nothing
Exit Do
They both achieve the same thing (breaking the loop), Exit Do does it more efficiently.
you may be after this (explanations in comments):
Sub XMAX()
Dim cel As Range
Dim oRng As Range: Set oRng = Sheets("Sheet2").Range("A1:A7500")
Dim oFoundRng As Range
Dim firstAddress As String
With Sheets("sheet1") ' reference "Sheet1" sheet
With .Range("f4", .Cells(.Rows.Count, "f").End(xlUp)) ' reference referenced sheet column "F" range from row 4 down to last not empty one
If WorksheetFunction.CountA(.Cells) > 0 Then ' if there's at least one not empty cell
For Each cel In .SpecialCells(xlCellTypeConstants) ' loop through referenced range not empty cells
Set oFoundRng = oRng.Find(what:=cel.Value, LookIn:=xlValues, lookat:=xlWhole) ' always specify at least 'LookIn' and 'LookAt' parameters, or they will be set as per last 'Find()' usage (even from Excel UI!)
If Not oFoundRng Is Nothing Then ' if a match found
firstAddress = oFoundRng.Address ' store first matched cell address
Do
Select Case UCase(oFoundRng.Offset(0, 1).Value2)
Case "ISAAC"
.Range("X" & cel.Row).Value = "X"
Case "YO"
.Range("V" & cel.Row).Value = "X"
Case "JAN"
.Range("U" & cel.Row).Value = "X"
Case Else
MsgBox oFoundRng.Value
End Select
Set oFoundRng = oRng.FindNext(oFoundRng) ' search for next occurrence
Loop While oFoundRng.Address <> firstAddress ' exit do when hitting fisr found cell again
End If
Next
End If
End With
End With
End Sub

Using "If cell contains" in VBA excel

I'm trying to write a macro where if there is a cell with the word "TOTAL" then it will input a dash in the cell below it. For example:
In the case above, I would want a dash in cell F7 (note: there could be any number of columns, so it will always be row 7 but not always column F).
I'm currently using this code, but it's not working and I can't figure out why.
Dim celltxt As String
Range("C6").Select
Selection.End(xlToRight).Select
celltxt = Selection.Text
If InStr(1, celltext, "TOTAL") > 0 Then
Range("C7").Select
Selection.End(xlToRight).Select
Selection.Value = "-"
End If
Help would be appreciated. Hopefully I'm not doing something stupid.
This will loop through all cells in a given range that you define ("RANGE TO SEARCH") and add dashes at the cell below using the Offset() method. As a best practice in VBA, you should never use the Select method.
Sub AddDashes()
Dim SrchRng As Range, cel As Range
Set SrchRng = Range("RANGE TO SEARCH")
For Each cel In SrchRng
If InStr(1, cel.Value, "TOTAL") > 0 Then
cel.Offset(1, 0).Value = "-"
End If
Next cel
End Sub
Private Sub Workbook_SheetChange(ByVal Sh As Object, ByVal Target As Range)
If Not Intersect(Target, Range("C6:ZZ6")) Is Nothing Then
If InStr(UCase(Target.Value), "TOTAL") > 0 Then
Target.Offset(1, 0) = "-"
End If
End If
End Sub
This will allow you to add columns dynamically and automatically insert a dash underneath any columns in the C row after 6 containing case insensitive "Total". Note: If you go past ZZ6, you will need to change the code, but this should get you where you need to go.
This does the same, enhanced with CONTAINS:
Function SingleCellExtract(LookupValue As String, LookupRange As Range, ColumnNumber As Integer, Char As String)
Dim I As Long
Dim xRet As String
For I = 1 To LookupRange.Columns(1).Cells.Count
If InStr(1, LookupRange.Cells(I, 1), LookupValue) > 0 Then
If xRet = "" Then
xRet = LookupRange.Cells(I, ColumnNumber) & Char
Else
xRet = xRet & "" & LookupRange.Cells(I, ColumnNumber) & Char
End If
End If
Next
SingleCellExtract = Left(xRet, Len(xRet) - 1)
End Function
Dim celltxt As String
Range("C6").Select
Selection.End(xlToRight).Select
celltxt = Selection.Text
If InStr(1, celltext, "TOTAL") > 0 Then
Range("C7").Select
Selection.End(xlToRight).Select
Selection.Value = "-"
End If
You declared "celltxt" and used "celltext" in the instr.
Is this what you are looking for?
If ActiveCell.Value == "Total" Then
ActiveCell.offset(1,0).Value = "-"
End If
Of you could do something like this
Dim celltxt As String
celltxt = ActiveSheet.Range("C6").Text
If InStr(1, celltxt, "Total") Then
ActiveCell.offset(1,0).Value = "-"
End If
Which is similar to what you have.
Requirement:
Find a cell containing the word TOTAL then to enter a dash in the cell below it.
Solution:
This solution uses the Find method of the Range object, as it seems appropriate to use it rather than brute force (For…Next loop).
For explanation and details about the method see Range.Find method (Excel)
Implementation:
In order to provide flexibility the Find method is wrapped in this function:
Function Range_ƒFind_Action(sWhat As String, rTrg As Range) As Boolean
Where:
sWhat: contains the string to search for
rTrg: is the range to be searched
The function returns True if any match is found, otherwise it returns False
Additionally, every time the function finds a match it passes the resulting range to the procedure Range_Find_Action to execute the required action, (i.e. "enter a dash in the cell below it"). The "required action" is in a separated procedure to allow for customization and flexibility.
This is how the function is called:
This test is searching for "total" to show the effect of the MatchCase:=False. The match can be made case sensitive by changing it to MatchCase:=True
Sub Range_Find_Action_TEST()
Dim sWhat As String, rTrg As Range
Dim sMsgbdy As String
sWhat = "total" 'String to search for (update as required)
Rem Set rTrg = ThisWorkbook.Worksheets("Sht(0)").UsedRange 'Range to Search (use this to search all used cells)
Set rTrg = ThisWorkbook.Worksheets("Sht(0)").Rows(6) 'Range to Search (update as required)
sMsgbdy = IIf(Range_ƒFind_Action(sWhat, rTrg), _
"Cells found were updated successfully", _
"No cells were found.")
MsgBox sMsgbdy, vbInformation, "Range_ƒFind_Action"
End Sub
This is the Find function
Function Range_ƒFind_Action(sWhat As String, rTrg As Range) As Boolean
Dim rCll As Range, s1st As String
With rTrg
Rem Set First Cell Found
Set rCll = .Find(What:=sWhat, After:=.Cells(1), _
LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False)
Rem Validate First Cell
If rCll Is Nothing Then Exit Function
s1st = rCll.Address
Rem Perform Action
Call Range_Find_Action(rCll)
Do
Rem Find Other Cells
Set rCll = .FindNext(After:=rCll)
Rem Validate Cell vs 1st Cell
If rCll.Address <> s1st Then Call Range_Find_Action(rCll)
Loop Until rCll.Address = s1st
End With
Rem Set Results
Range_ƒFind_Action = True
End Function
This is the Action procedure
Sub Range_Find_Action(rCll)
rCll.Offset(1).Value2 = Chr(167) 'Update as required - Using `§` instead of "-" for visibilty purposes
End Sub

Excel VBA lookup issue

I'm new to excel VBA though I tried putting efforts but of no luck.
Problem Statement:
I have a sheet with Source (color white) row and Destination (color Yellow) row, for each source there is a corresponding destination row in next line. I have to look for an application name which user input at the beginning and will search throughout the sheet (more than 10000 rows) in column 6 and it must extract the source row also if found in destination and destination row also if found in source row in sheet2.
And also one cell may have many application name within it so it should trim all other app names from that cell and leave only the searched app name.
Here is the partial code i tried :
Sub GetInterfaceCounts()
Dim RANGEBOTTOM As String
Dim cell
Dim strAction As String
Dim intAdd As Integer
Dim strName As String
intAdd = 0
RANGEBOTTOM = "G700"
strName = InputBox(Prompt:="Please enter the application name.", _
Title:="Application Name", Default:="Application")
For Each cell In Range("G2:" & RANGEBOTTOM)
strAction = cell.Value
If InStr(1, strAction, strName) <> 0 Then
intAdd = intAdd + 1
End If
Next
MsgBox "Total number of " & strName & " counts are :" & CStr(intAdd)
GetMS4AppInventory (strName)
End Sub
Sub GetMS4AppInventory(strName As String)
Dim strAction
Dim intAdd As Integer
Dim RowIndex As Integer
RowIndex = 0
Sheets("Sheet1").Select
'For Each cell In Range("G2:G700")
With Worksheets("Sheet1").Range("G2:G700")
Set strAction = .Find(strName, LookIn:=xlValues)
'strAction = cell.Value
If Not strAction Is Nothing Then
Do
If InStr(1, strAction, strName) <> 0 Then
Rows(strAction.Row).Select
Selection.Copy
Sheets("MS4Inventory").Select
Rows(RowIndex + 1).Select
Selection.Insert Shift:=xlDown
Rows(RowIndex + 2).Select
Application.CutCopyMode = False
Selection.Insert Shift:=xlDown, CopyOrigin:=xlFormatFromLeftOrAbove
Cells(RowIndex + 3, 1).Select
End If
Set strAction = .FindNext(strAction) //gets hanged here go to infinite loop
Loop While Not strAction Is Nothing
End If
End With
End Sub
If anybody could help me it will be great else manually doing seggregation of inventory will suck me.
Regards,
Vijay
When you use FindNext, you have to store the first found cell's address and compare it. strAction, in your example, will never be Nothing because FindNext will keep finding the first cell that had it.
I'm not sure how your white and yellow rows factor into this, but here's a basic structure for finding cells and copy their rows. Maybe you can modify it to your needs or clarify what your existing data looks like.
Sub GetInterfaceCounts()
Dim sName As String
Dim rFound As Range
Dim lCount As Long
Dim sFirstAdd As String
'Get the application name from the user
sName = InputBox(Prompt:="Please enter the application name.", _
Title:="Application Name", Default:="Application")
'if the user doesn't press cancel
If Len(sName) > 0 Then
'Find the first instance of the application
Set rFound = Sheet1.Columns(7).Find(sName, , xlValues, xlPart, , , False)
'if something was found
If Not rFound Is Nothing Then
'Remember the first address where it was found
sFirstAdd = rFound.Address
Do
lCount = lCount + 1
'Copy the entirerow to the other sheet
rFound.EntireRow.Copy _
rFound.Parent.Parent.Sheets("MS4Inventory").Cells(lCount, 1).EntireRow
'Find the next instance
Set rFound = Sheet1.Columns(7).FindNext(rFound)
'if we've looped around to the first found, then get out
Loop Until rFound.Address = sFirstAdd
End If
MsgBox "Total number of " & sName & " counts are :" & lCount
End If
End Sub

Resources