How to check if string appears in worksheet *twice* Excel VBA? - vba

I'm trying to check if a certain string appears on a given sheet twice. So far, I'm only able to check if the string appears once:
For Each curr In wb.Worksheets(1).UsedRange
If InStr(1, curr.Value, searchString) > 0 Then
MsgBox ("searchString appears once")
End If
Next
How do I check the UsedRange to see if the value appears twice? This needs to be a macro (so far I've found formulas that do this).

If WorksheetFunction.CountIf(Worksheets(1).UsedRange, "*" & searchString & "*") > 1 Then
MsgBox searchString & " appears more than once"
End If

Set a flag when you find the first match, then check to see if the flag is set:
Dim first As Boolean
For Each curr In wb.Worksheets(1).UsedRange
If InStr(1, curr.Value, searchString) > 0 Then
If first Then MsgBox ("searchString appears twice")
first = True
End If
Next

Related

RunTime Error 13, Type Mismatch MsgBox Cancel

Getting a Run-Time Error 13 type mistmatch error when clicking cancel on a message box.
I tried making the following script to handle if a message box is empty, however upon bug checking, clicking cancel on the message box throws it all out.
Any ideas?
Private Sub ChangeDebtAmounts_Click()
Dim Debt1 As Integer, Debt2 As Integer, Debt3 As Integer, Debt4 As Integer
Dim D1Range As String, D2Range As String, D3Range As String, D4Range As String
D1Range = ActiveSheet.Range("Y15")
D2Range = ActiveSheet.Range("Y16")
D3Range = ActiveSheet.Range("Y17")
D4Range = ActiveSheet.Range("Y18")
Debt1 = InputBox("Please Enter in the account limit for " & D1Range)
If Debt1 = "" Then
MsgBox ("Setting " & D1Range & " to Zero, No Value Entered")
Else
Range("AA15").Value = Debt1 - Range("S58")
End If
End
End Sub
The type mismatch is with the InputBox rather than the MsgBox. To fix it, it is enough to change Dim Debt1 As Integer to Dim Debt1 As Variant. Also, you are using MsgBox as a sub rather than a function so the correct syntax should be
MsgBox "Setting " & D1Range & " to Zero, No Value Entered"
rather than
MsgBox ("Setting " & D1Range & " to Zero, No Value Entered")
In this case the parentheses are harmless, but if you try to give additional arguments to MsgBox while using it as a sub then you will get a syntax error.
Here's a slightly different take on your question. See my comments within the code.
(It's longer just because of the comments; optionally, you can remove any lines of comments, as well as any other blank lines.)
Private Sub ChangeDebtAmounts_Click()
Dim Debt1, Debt2, Debt3, Debt4 'data type "Variant" is assumed
Dim D1Range As String,D2Range As String,D3Range As String,D4Range As String
'by using a "With" statement, you can use "." instead of "ActiveSheet."
With ActiveSheet
D1Range = .Range("Y15")
D2Range = .Range("Y16")
D3Range = .Range("Y17")
D4Range = .Range("Y18")
'I added a title to the dialog and a default value of zero
Debt1=InputBox("Enter the account limit for " & D1Range, "Limit?" ,0)
'Check user response:
If Debt1 = "" Or Debt1 = 0 Then
'User clicked cancel or entered zero.
MsgBox "Setting " & D1Range & " to Zero, No Value Entered"
'I assume your next step is to set the input value to zero:
Debt1 = 0
Else
'you don't need to specify ".Value" in most cases (it's assumed)
'also:by using the "." we're referring to ActiveSheet again.
.Range("AA15") = Debt1 - .Range("S58")
End If
End With '(the end of "With ActiveSheet")
End Sub
A couple other thoughts:
it appears like you're going to use different variables for each InputBox but this is not necessary: you can re-use the same variable in this case, without issue.
ActiveSheet just refers to "whichever worksheet (tab) happens to be open when the code is run". It's a good idea to explicitly refer to a specific worksheet, to prevent potential problems in the future.
For example if your cells such as Y15 are on worksheet Sheet1, you could replace ActiveSheet with Sheets("Sheet1").
Alternate method (loop through all 4 cells)
These methods are for demonstration only - if you already have your solution figured out, stick with that, there's no point in wasting time! These are just to show other ways to do the same thing.
Just for fun, here's another alternate method, that loops through all 4 cells Y15:Y18 and repeats the same MsgBox's.
I wasn't sure what happens with the other 3 values the user enters, so I left those blank.
Private Sub demo_Alternate()
Dim userInput As Variant, arr As Variant, myCell
With Sheets("Sheet1") '<<<<<< change this to actual worksheet name
arr = .Range("Y15:Y18") ' arr(1) to arr(4) are now cell references
For Each myCell In arr
userInput = InputBox("Enter account limit for " & myCell, "Limit?", 0)
If userInput = "" Or userInput = 0 Then 'Cancelled or 0 entered
MsgBox "Setting " & myCell & " to Zero, No Value Entered"
userInput = 0
Else
Select Case Split(myCell.Address, "$")(2)
Case 15 'do what you need to for cell Y15
Range("AA15") = userInput - Range("S58")
Case 16
'do what you need to for cell Y16
Case 17
'do what you need to for cell Y16
Case 18
'do what you need to for cell Y16
End Select
End If
Next myCell 'loop to next cell
End With
End Sub
OR, if all four cells are getting from S58 and put into column AA of the same row, like:
...if your end-goal is the pattern:
AA15 = {Y15 or UserEntry} - S58
AA16 = {Y16 or UserEntry} - S58
AA17 = {Y17 or UserEntry} - S58
AA18 = {Y18 or UserEntry} - S58
...then something like this could work (and is even more compact).
Private Sub demo_Alternate2()
Dim userInput As Variant, arr As Variant, myCell, rowNum As Long
With Sheets("Sheet1") '<<<<<<<<<<<<< change this to actual worksheet name
arr = .Range("Y15:Y18") ' arr(1) to arr(4) are now cell references
For Each myCell In arr
userInput = InputBox("Enter account limit for " & myCell, "Limit?", 0)
If userInput = "" Or userInput = 0 Then 'Cancelled or 0 entered
MsgBox "Setting " & myCell & " to Zero, No Value Entered"
Else
rowNum = Split(myCell.Address, "$")(2)
Range("AA" & rowNum) = userInput - Range("S58")
End If
Next myCell
End With
End Sub
One noteworthy technique used here is the use of an array (arr) to read multiple cell values at once instead of a separate line for each cell input.
arr = .Range("Y15:Y18")
...assigns the four cells to the array so you can refer to the array as if:
arr(1) = Y15
arr(2) = Y16
arr(3) = Y17
arr(4) = Y18

excel VBA only keep the certain part of a the text in a cell

I have a report that is imported into excel every day, and the last column of information "Z", is all of the comments that have been left by previous agents working on the account. I am only interested in the last comment, but it can be of any length, so i cant just grab x amount of characters.
Question: Is there a way to only pull the last comment based on the criteria of the comment? (every comment ends with the username, date, and time-stamp:
Example of a cell:
Example of agent1 comment. [USERNAME1-xx/xx/xxxx xx:xx:xx PM] - Example of agent2 comment. [USERNAME2-xx/xx/xxxx xx:xx:xx PM])
In this scenario, the only text that i would want in the cell would be: "Example of agent2 comment.".
For the record, all of the imported report starts on "A2".
Guess I shouldn't do this as you haven't shown what you've tried yet, but this code should do the trick.
Enter in a cell: =ExtractLastComment(H3) where H3 contains the comment.
'Use this procedure to run on a range of cells.
'The result is placed one cell to the right of the comment: "Offset(, 1)"
Public Sub CommentsInColumn()
Dim rTarget As Range
Dim rCell As Range
Set rTarget = ThisWorkbook.Worksheets("Sheet1").Range("A2:A30")
For Each rCell In rTarget
rCell.Offset(, 1) = ExtractLastComment(rCell)
Next rCell
End Sub
Public Function ExtractLastComment(Target As Range) As Variant
Dim sCommentText As String
If HasComment(Target) Then
'Get the comment text.
sCommentText = Target.Comment.Text
If InStrRev(sCommentText, "[") <> 0 Then
'Find the last open bracket and take everything to the left of it.
sCommentText = Trim(Left(sCommentText, InStrRev(sCommentText, "[") - 1))
'Any closing brackets left?
If InStrRev(sCommentText, "]") <> 0 Then
'Take everything from last closing bracket to end of text.
sCommentText = Mid(sCommentText, InStrRev(sCommentText, "]") + 4)
End If
ExtractLastComment = sCommentText
Else
ExtractLastComment = CVErr(xlErrValue)
End If
Else
'There isn't a comment in the cell, return a !#NULL error.
ExtractLastComment = CVErr(xlErrNull)
End If
End Function
Public Function HasComment(Target As Range) As Boolean
On Error GoTo ERROR_HANDLER
If Target.Cells.Count = 1 Then
With Target
HasComment = Not .Comment Is Nothing
End With
Else
Err.Raise 513, "HasComment()", "Argument must reference single cell."
End If
On Error GoTo 0
Exit Function
ERROR_HANDLER:
Select Case Err.Number
Case Else
MsgBox "Error " & Err.Number & vbCr & _
" (" & Err.Description & ") in procedure HasComment."
Err.Clear
Application.EnableEvents = True
End Select
End Function

Searching an Excel column for containing text [duplicate]

I currently have an Excel file with 1 column and many rows. The column holds a first name, last name, and possibly a middle name (EXAMPLE: John Abe Smith). I am writing a macro that has 1 textbox and 1 button. In the excel sheet I have a couple names:
Column A
--------
John Abe Smith
Cindy Troll Bee
Randy Row
Joe Jumbo
Katie Kool Kat
I want to write a macro that when I type something in the textbox and click the button, it will look in this column for the name. If it finds it, then just say "found" in a message box.
I want to use the wildcard "*" when searching the names, but I do not know how. I currently have some code like this, but the wildcard does not work:
Private Sub search_Click()
For firstloop = 3 To 10
If Range("G" & firstloop).Text = name.Text & "*" Then
MsgBox "Found!"
Exit Sub
Else
MsgBox "NOT FOUND"
End If
Next
End Sub
For example, let's say I type in "Troll" in the text box and I click the button. I want the loop to go through the column to find anything with "Troll" in it. The result from the example data would be just Cindy Troll Bee.
How could I go about doing this?
You can use Like operator (case-sensitive):
Private Sub search_Click()
For firstloop = 3 To 10
If Range("G" & firstloop).Text Like name.Text & "*" Then
MsgBox "Found!"
Exit Sub
Else
MsgBox "NOT FOUND"
End If
Next
End Sub
for case-insensitive search use:
If UCase(Range("G" & firstloop).Text) Like UCase(name.Text) & "*" Then
Also if you want to determine whether cell contains text (not only starts with text), you can use (case-sensitive):
If InStr(1, Range("G" & firstloop).Text, name.Text) > 0 Then
or (case-insensitive)
If InStr(1, Range("G" & firstloop).Text, name.Text, vbTextCompare) > 0 Then
UPD:
If the point only to show msgbox, then I'd suggest to use Application.Match:
Private Sub search_Click()
If Not IsError(Application.Match("abc" & "*", Range("G3:G10"), 0)) Then
MsgBox "Found!"
Else
MsgBox "NOT FOUND"
End If
End Sub
You can also avoid a loop and use the Range.Find Method. You can pass as option if you want it case sensitive or not. (By default it is not case sensitive.)
Set rngFound= Range("G" & firstloop).Find(name.Text & "*")
If rngFound Is Nothing Then
MsgBox "Not Found!"
Else
MsgBox "FOUND"
End If

Macro to change tab name based on cell value with special duplicate handling

I have a macro that changed a tab name based on cell value (A4) which contains a formula to give the sheet a unique name, but I wanted to see if it was possible to create special case handling occurrences when there's a duplicate. So here's the code:
Sub RenameFromA4()
Dim Msg As String, i As Integer
For i = 5 To Sheets.Count
If Sheets(i).Range("A4").Value = "" Then
Msg = "Sheet " & i & "(" & Sheets(i).Name & ") has no value in A4. Fix sheet, then rerun."
MsgBox Msg, vbExclamation
Exit Sub
Else
On Error GoTo ErrSheetName
Sheets(i).Name = Sheets(i).Range("A4").Value
On Error GoTo 0
End If
Next i
Exit Sub
ErrSheetName: Msg = "Sheet " & i & "(" & Sheets(i).Name & ") could not be renamed. Check if name already used."
MsgBox Msg, vbExclamation
End Sub
The trouble I run into is sometimes duplicates can arise and error out my whole macro where it comes to a complete halt. So I want to add a sequence that when the macro encounters a duplicate add the following formula in cell B3: ="IF(AND(C4="",D4="",D3="",C3=""),TRIM((MID(A2,FIND(":",A2)+2,20))),"")&IF(IFERROR(FIND("West",A2),0)>0," W","")&" "&TRIM(RIGHT(SUBSTITUTE(A2," ",REPT(" ",255)),255))"
and pick from the error or just go back to rerunning the macro.
Any insight on how I can't structure this will be helpful.
You can explicitly check for the existence of a sheet named the same as the value in B4 by using a function like what is described here: Test or check if sheet exists. Then, you can insert something like the following between your On Error... and Sheets(i).Name...:
On Error GoTo ErrSheetName
If SheetExists(Sheets(i).Range("A4").Value) Then
Sheets(i).Range("B3").Formula = "=IF(AND(C4="",D4="",D3="",C3=""),TRIM((MID(A2,FIND(": ",A2)+2,20))),"")&IF(IFERROR(FIND("West ",A2),0)>0,"W ","")&""&TRIM(RIGHT(SUBSTITUTE(A2,"",REPT("",255)),255))"
End If
Sheets(i).Name = Sheets(i).Range("A4").Value

VBA Textbox value loop vlookup for number of times value is found

I am creating a table in my spreadsheet that contains categories, questions and answers for a quiz.
The user is presented with a form, allowing them to navigate around the workbook easily, this also includes a textbox option, allowing them to search for a phrase if they are unsure what category the question/answer may fall into.
I have generated a vlook up to pull from the table of categories/questions and answers to the user on a different worksheet.
I have also generated a count, so I am able to identify how many times this work appears across the quiz table.
My problem is I am struggling to develop a loop so that if the key phrase is found 6 times for example, i want 6 questions and answers to be listed to the user. Currently it is only pulling the final time it is found.
My current code includes the following:
Private Sub CommandButton1_Click()
If Len(search_text) = 0 Then
MsgBox "Please enter a key word to search for!", vbCritical
End If
Dim wordCount As Integer
wordCount = Application.WorksheetFunction.CountIf(Sheet1.Range("A2:c600"), "*" & search_text.Value & "*")
'Else: wordCount = WorksheetFunction.CountIf(Sheet1.Range("A2:c600"), search_text.Value)
If wordCount = 0 Then
MsgBox "No match found"
Else
Sheet2.Range("a7").Value = WorksheetFunction.VLookup("*" & search_text.Value & "*", Sheet1.Range("A2:c600"), 3, False)
Sheet2.Range("b7") = wordCount
End If
End Sub
Any advice on implementing a loop and to allow the question/answer to be printed one after another would be very much appreciated.
I have read many other question pages about this and none seem to match what I am trying to do.
Many thanks in advance
I use a combination of Find and FindNext to search through a range of cells for the term entered in the search_text input field. I added comments to my code to better help you understand what exactly is going on.
I don't know exactly what you need to do with the results when you find them, for now I just display a message box showing the match. We can work on what to actually do with the results if you want to clarify in the comments what exactly you want.
This code assumes you have a worksheet named Results
Private Sub CommandButton1_Click()
Dim rngResult As Range
Dim strFirstAddress As String
Dim i As Long
If Len(search_text.Text) = 0 Then
MsgBox "Please enter a key word to search for!", _
vbCritical
'Stop code exeuction if no search
'term is entered
Exit Sub
End If
'Clear the previous results range
Sheets("Results").Range("A2:C600").ClearContents
'Set i to row 2 of the results worksheet
i = 2
'Look in range A2:C600 of Sheet1
With Sheet1.Range("A2:C600")
'Perform the initial find
Set rngResult = .Find(What:=search_text.Text, LookAt:=xlPart)
'Check to ensure that the term is found
If Not rngResult Is Nothing Then
'Grab the cell address of the first match
'This will help to avoid an infinite loop
strFirstAddress = rngResult.Address
'Continue Searching
Do
'Display the output to you
'MsgBox "Matched '" & search_text.Text & "' to " & rngResult.Value & " in cell " & rngResult.Address
'Put the result on the results page
Sheets("Results").Range("A" & i & ":C" & i).Value = Range("A" & rngResult.Row & ":C" & rngResult.Row).Value
i = i + 1
'Move on to the next result
Set rngResult = .FindNext(rngResult)
'Break out of the loop when we return to the starting point of the search
Loop While Not rngResult Is Nothing And rngResult.Address <> strFirstAddress
End If
End With
'Clean up variables
Set rngResult = Nothing
End Sub