Using .Find() to find specific text in column - vba

I'm having trouble making sure that my code uses what the end user inputs to find a set of data pertaining to that value and continues with the code there. For example, if the user were to input "V-" as the prefix to the tag number, in theory cell A7 should be selected after the code is complete. However, the code proceeds to run line "MsgBox "No blank cell was found below a tag number with prefix " & str & ".", vbExclamation" and select cell A3 due to the fact that it contains "V-" in the cell. I tried changing the Matchcase to true but it did not help. I also do not want the entered value to be case sensitive.
Code being used:
Private Sub Worksheet_Activate()
Dim msg As String
Dim Cell As Range
Dim str As String, firstcell As String
msg = "Would you like to find the next available tag number?"
result = MsgBox(msg, vbYesNo)
If result = vbYes Then
str = Application.InputBox("Enter The Tag Number Prefix ", "Prefix To Tag Number")
If str = "" Then Exit Sub
If Right(str, 1) <> "-" Then str = str & "-"
With Range("A:A")
Set Cell = .Find(str, lookat:=xlPart, MatchCase:=False)
If Not Cell Is Nothing Then
firstcell = Cell.Address
Do
If Cell.Offset(1, 0) = "" Then
Cell.Offset(1, 0).Select
Exit Sub
ElseIf InStr(LCase(Cell.Offset(1, 0)), LCase(str)) = 0 Then
Cell.Select
MsgBox "No blank cell was found below a tag number with prefix " & str & ".", vbExclamation
Exit Sub
End If
Set Cell = .FindNext(Cell)
Loop While Not Cell Is Nothing And firstcell <> Cell.Address
End If
End With
Else
Cancel = True
End If
End Sub

If you want to find cells whose content begins with (e.g.) "V-" then
Set Cell = .Find(str & "*", lookat:=xlWhole, MatchCase:=False)
For the data below:
Sub tester()
With ActiveSheet.Columns(1)
Debug.Print .Find("C-" & "*", lookat:=xlWhole, _
MatchCase:=False).Address() '>> $A$3
Debug.Print .Find("V-" & "*", lookat:=xlWhole, _
MatchCase:=False).Address() '>> $A$5
End With
End Sub

Related

VBA Word. How to find first empty cell in Word table?

I've been trying to find the first empty cell in Word table using VBA.
The code which I've put below finds all the empty cells instead I want to find the first one after filled one. How to solve this problem?
For Each oRow In Selection.Tables(1).Rows
For Each oCell In oRow.Cells
If Selection.Text = Chr(13) & Chr(7) Then
oCell.Select
'Selection.PasteSpecial DataType:=wdPasteText
MsgBox oCell.RowIndex & " " & oCell.ColumnIndex & " is empty."
End If
Next oCell
Next oRow
Is this what you had in mind?
Sub FindNextBlank()
Dim Tbl As Table
Dim TblRow As Row
Dim HasText As Boolean
Dim LookForText As Boolean, Done As Boolean
Dim R As Long, C As Long
Dim Txt As String
LookForText = True
With ThisDocument.Tables(1)
For R = 1 To .Rows.Count
Set TblRow = .Rows(R)
For C = 1 To TblRow.Cells.Count
HasText = (Len(TblRow.Cells(C).Range.Text) > 2)
If HasText = LookForText Then
If LookForText Then
LookForText = Not LookForText
Else
Done = True
TblRow.Cells(C).Range.Select
Exit For
End If
End If
Next C
If Done Then Exit For
Next R
If Done Then
Txt = "Cell #" & C & " in row " & R & " is free."
Else
Txt = "No free cell was found that" & vbCr & _
" follows one that has text."""
End If
End With
MsgBox Txt, vbInformation, "Search result"
End Sub
For ... Each is faster but I instinctively distrust it because the sequence of items in them is usually determined by the sequence of their creation. That may or may not be top to bottom, left to right. Calling cells by their coordinates may take a little longer but you retain control of the sequence.
As you may have discovered, determining an empty cell in Word is not as straightforward as it might appear. The code below looks for the first cell where the length of the text in the cell is 1 after removing any spaces, tabs and vbCr. You might extend this to also look for vbLF, manual line breaks and other characters that might be in a cell but not visible if you have view text markers turned off.
The .Cells method of a table range is the most appropriate tool to use here because it will work even if the table has merged cells. Searching a table using the cell coordinates will fail if there are merged cells in the table. Using the .Cells method the table is searched from Top Left to Bottom right (row by column).
Option Explicit
Sub Test()
Dim myCell As Word.Range
Set myCell = FirstEmptyCell(ActiveDocument.Tables(1).Range)
myCell.Select
End Sub
' Returns the first cell that has a text length of 1
' after removing spaces and tab characters from the cell text
Public Function FirstEmptyCell(ByVal TableRange As Word.Range) As Word.Range
Dim myCell As Word.Cell
For Each myCell In TableRange.Tables(1).Range.Cells
Dim CellText As String
CellText = myCell.Range.Text
CellText = Replace(CellText, vbTab, vbNullString)
CellText = Replace(CellText, " ", vbNullString)
CellText = Replace(CellText, vbCr, vbNullString)
If Len(CellText) = 1 Then
Set FirstEmptyCell = myCell.Range
Exit Function
End If
Next
End Function
The solution is really much simpler than the other 'answers' suggest:
Dim i As Long
With Selection
If .Information(wdWithInTable) = True Then
With .Tables(1).Range
For i = 1 To .Cells.Count
With .Cells(i)
If Len(.Range.Text) = 2 Then
MsgBox " Row " & .RowIndex & ", Column " & .ColumnIndex & " is empty."
.Range.PasteSpecial DataType:=wdPasteText
Exit For
End If
End With
Next
End With
Else
MsgBox "No table selected", vbExclamation
End If
End With
I've even added some error checking.

Appending to a cell value in VBA

I'm sure there's an obvious answer here, but I'm stuck. This part in particular is throwing 424: Object Required. The really odd part, to me, is that it does successfully append 0s to the column, but then halts, and doesn't continue.
If cellLen < 9 Then
Set C.Value = 0 & C.Value
End If
The rest of the code is below for clarity. In case it's not clear, this is the intended code flow:
Grabs named fields
Copies those columns to a new sheet
Renames them and deletes the original sheet
Creates some new sheets for use with a different script
Searches for missing leading 0s in a specific column
Adds them back in (this is the part the breaks)
Deletes rows where that specific column's cell value is 0
Pulls that cleaned-up column out to a new file and saves it
Sub Cleanup_Mapwise_Import()
Dim targetCols As Variant
Dim replColNames As Variant
Dim index As Integer
Dim found As Range
Dim counter As Integer
Dim headerIndex As Integer
Dim question As Integer
Dim rowCount As Variant
Dim colNum As Variant
Dim colLetter As Variant
Dim C As Range
Dim cellLen As Integer
' Add or remove fields to be copied here
targetCols = Array("gs_account_number", "gs_meter_number", "gs_amr_identification", _
"gs_amr_phase", "gs_city", "Name", "Phase", _
"gs_rate_schedule", "gs_service_address", _
"gs_service_map_location", "gs_service_number")
' Put the same fields from above in the desired order here, with the desired name
replColNames = Array("Acct #", "Meter #", "AMR ID", "AMR Phase", "City", _
"Name", "Phase", "Rate", "Address", "Srv Map Loc", "Srv Num")
counter = 1
ActiveSheet.Range("A1").Select
' This counts the number of columns in the source array and sets the index to that value
For index = LBound(targetCols) To UBound(targetCols)
Set found = Rows("1:1").Find(targetCols(index), LookIn:=xlValues, LookAt:=xlWhole, _
SearchOrder:=xlByColumns, SearchDirection:=xlNext, MatchCase:=False)
' This is basically an insertion sort, and ends up with the columns in A:K
If Not found Is Nothing Then
If found.Column <> counter Then
found.EntireColumn.Cut
Columns(counter).Insert shift:=xlToRight
Application.CutCopyMode = False
End If
counter = counter + 1
End If
Next index
' There is a more dynamic way of doing this, using index
' As it is, replace A:K with the range of actual data
' PROTIP: targetCols is 1-indexed, and has 11 entries -->
' A:K encompasses that entire array -->
' Add/subtract 1 for each entry you add/remove
Range("A:K").Cut
Set TargetSheet = Sheets.Add(After:=Sheets(Sheets.Count))
TargetSheet.Name = "Contributors"
Range("A:K").Insert
question = MsgBox("Do you want to delete the original sheet?", vbYesNo + vbQuestion, "Delete Sheet")
If question = vbYes Then
Sheets(1).Activate
Sheets(1).Delete
Else
End If
Sheets.Add.Name = "Data"
Sheets("Contributors").Move After:=Sheets("Data")
Sheets.Add.Name = "Graph"
Sheets("Graph").Move After:=Sheets("Contributors")
Sheets("Data").Activate
Range("A1").Value = "Date/Time"
Range("B1").Value = "kW"
Range("C1").Value = "Amps"
' Yes, counter is 0-indexed here, and 1-indexed previously
' headerIndex does an absolute count of 0 To # targetCols, whereas index is relative
' If you change these, there is a non-zero chance that the For will throw an error
counter = 0
Sheets("Contributors").Activate
ActiveSheet.Range("A1").Select
For headerIndex = 0 To (UBound(targetCols) - LBound(targetCols))
ActiveCell.Value = replColNames(counter)
' If you don't use a Range, it fits columns based on headers, which isn't large enough
' A1:Z500 is a big enough sample to prevent that
ActiveCell.Range("A1:Z500").Columns.AutoFit
ActiveCell.Offset(0, 1).Select
counter = counter + 1
Next headerIndex
' Find column number with meters numbers, then assign its corresponding letter value
colNum = Application.WorksheetFunction.Match("Meter #", Range("A1:ZZ1"), 0)
colLetter = (Split(Cells(, colNum).Address, "$")(1))
rowCount = Range(colLetter & Rows.Count).End(xlUp).Row
'Range(colLetter & "2:" & colLetter & rowCount).Select
'Selection.SpecialCells(xlCellTypeBlanks).Select
'Selection.Delete Shift:=xlUp
' Meter numbers are 9 digits, so if one is shorter, assume a trimmed leading 0 and append it
For Each C In Range(colLetter & "2:" & colLetter & rowCount).Cells
' If cell type isn't set to text, the 0s will be non-visible, which while not an issue for the CSV, is confusing
' Note that this does not persist, as CSVs have no way of saving Excel's formatting
C.NumberFormat = "#"
cellLen = Len(C.Value)
If C.Value = "0" Or cellLen = 0 Then
C.Delete shift:=xlUp
End If
If cellLen < 9 Then
Set C.Value = 0 & C.Value
End If
Next C
question = MsgBox("Do you want to create a CSV file with meter numbers for use with MDMS?", vbYesNo + vbQuestion, "MDMS File")
If question = vbYes Then
' Call CopyMeters for use with MDMS
Sheets("Contributors").Activate
CopyMeters
Else
End If
End Sub
Sub CopyMeters()
Dim index As Integer
Dim fileSaveName As Variant
Dim rowCount As Variant
Dim colNum As Variant
Dim colLetter As Variant
Dim cellLen As Integer
colNum = Application.WorksheetFunction.Match("Meter #", Range("A1:ZZ1"), 0)
colLetter = (Split(Cells(, colNum).Address, "$")(1))
rowCount = Range(colLetter & Rows.Count).End(xlUp).Row
MsgBox ("Filename will automatically be appended with ""Meter List""")
fileSaveName = Split(ActiveWorkbook.Name, ".")
fileSaveName = fileSaveName(LBound(fileSaveName)) & " Meter List"
'For Each C In Range(colLetter & "2:" & colLetter & rowCount)
' C.NumberFormat = "#"
' cellLen = Len(C)
' If C.Value = "0" Or cellLen = 0 Then
' C.Delete shift:=xlUp
' End If
' If cellLen < 9 And cellLen <> 0 Then
' C.Value = "0" & C.Value
' End If
'Next C
Range(colLetter & "1:" & colLetter & rowCount).EntireColumn.Copy
Set newBook = Workbooks.Add
newBook.Worksheets("Sheet1").Range("A1").PasteSpecial (xlPasteAll)
Selection.Replace What:="0", Replacement:="", LookAt:=xlWhole, _
SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
Selection.Columns.AutoFit
newBook.SaveAs Filename:=fileSaveName, FileFormat:=xlCSV, CreateBackup:=False
End Sub
The error message is telling you that C is not an object. Therefore, you do not need the Set statement. Change your code to this:
If cellLen < 9 Then
C.Value = 0 & C.Value
End If
Why not just change the numberformat on the range? Or use a function for the value? A function would be something like
Public Function FormatValues(ByVal Input as String) as String
If Input <> vbNullString Then FormatValues = Format(Input, "000000000")
End Function
And it would be called like:
C.Value = FormatValues(C.Value)
But, if you're strictly interested in what the value looks like, and not as much as what the value is (since the leading zero will only be retained for strings) you could do something like this:
Public Sub FixFormats()
ThisWorkbook.Sheets("SomeSheet").Columns("A").NumberFormat = "000000000")
End Sub
This would format Column A of Worksheet "SomeSheet" to be of the format "0000000" which means numbers would look like "000000001", "000000002" so on so forth, regardless of whether something like "2" was actually entered.

Excel VBA: FindNext in nested loops

I am trying to create a loop using the .Find function within another loop which is already using .Find. I want to search for strings that I have saved in an array.
For example, these are the string values saved in the array strItem in Sheet1.
"unit1", "unit2", "unit3"
I would like to search them one by one from Sheet2. Sheet2 looks like this:
unit1
unit2
unit3
unit1.pdf
text1
subject1
subject2
subject3
text2
=========
unit2.pdf
text1
subject1
subject2
subject3
text2
=========
unit3.pdf
text1
subject1
subject2
subject3
text2
=========
After searching for "unit1.pdf", I search all cells below it for "subject", and get cell values of subject1, 2, and 3. The search for "subject" cells should stop at the next cell which contains "====".
Next I search for "unit2", and if found search for "subject" cells under it as before. Again, stop at the cell containing "====". And so on.
In my code, what I am trying to do was
Search for the string "unit".
Use its .row as the range to start searching for "subject".
Return all subjects until the cell contains "====". This is a part of my code that I can't really make to work.
Code:
Wb2.Sheets("Sheet2").Activate
With Wb2.Sheets("Sheet2").Range("A1:A1048575")
For Each strItem In arrExcelValues
myStr = strItem & ".pdf"
Set p = .Find(What:=myStr, LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False)
If Not p Is Nothing Then
firstAddress = p.Address
Do
myStr2 = p.row
strStart = "A" & myStr2
strEnd = "A1048575"
With Wb2.Sheets("Sheet2").Range(strStart, strEnd)
Set p1 = .Find(What:="Subject", LookIn:=xlValues, _
LookAt:=xlPart, SearchOrder:=xlByRows, MatchCase:=False)
If Not p1 Is Nothing Then
firstAddress = p1.Address
Do
myStr2 = myStr2 + 1
If p1.Offset(myStr2, 0).Value = "====" Then
Exit Do
Else
MsgBox p1.Value & strItem
End If
Set p1 = .FindNext(p1)
Loop While Not p1 Is Nothing And p1.Address <> firstAddress
Else
MsgBox "Not found"
End If
End With
Set p = .FindNext(p)
Loop While Not p Is Nothing And p.Address <> firstAddress
Else
MsgBox "Not found"
End If
Next
End With
You're not far off, but there are a couple of things to think about:
It seems you know the order of your data, you can use this to make life easier than using Find on the entire column.
You cannot use nested With statements unless you are going further into the child elements. It is good you are trying to fully qualify things, but be careful. For instance,
' This is okay
With ThisWorkbook.Sheets("Sheet2")
With .Range("A1")
MsbBox .Value
End With
With .Range("A2")
MsgBox .Value
End With
End With
' This is not okay, and present in your code
With ThisWorkbook.Sheets("Sheet2").Range("A1")
MsgBox .Value
With ThisWorkbook.Sheets("Sheet2").Range("A2")
Msgbox .Value
End With
End With
I have taken the ideas in your code, and re-written it to be a bit clearer, and hopefully achieve what you want. See the comments for details:
Dim Wb2 As Workbook
Dim lastRow As Long
Set Wb2 = ThisWorkbook
' Get last used row in sheet, so search isn't on entire column
lastRow = Wb2.Sheets("Sheet2").UsedRange.Rows.Count
' Set up array of "unit" values
Dim arrExcelValues() As String
arrExcelValues = Split("unit1,unit2,unit3", ",")
' Declare variables
Dim pdfCell As Range
Dim eqCell As Range
Dim eqRow As Long
eqRow = 1
Dim subjCell As Range
Dim strItem As Variant
' Loop over unit array
With Wb2.Sheets("Sheet2")
For Each strItem In arrExcelValues
' Find the next "unitX.pdf" cell after the last equals row (equals row starts at 1)
Set pdfCell = .Range("A" & eqRow, "A" & lastRow).Find(what:=strItem & ".pdf", lookat:=xlPart)
If Not pdfCell Is Nothing Then
' pdf row found, find next equals row, store row value or use last row
Set eqCell = .Range("A" & pdfCell.Row, "A" & lastRow).Find(what:="====", lookat:=xlPart)
If eqCell Is Nothing Then
eqRow = lastRow
Else
eqRow = eqCell.Row
End If
' Loop through cells between pdf row and equals row
For Each subjCell In .Range("A" & pdfCell.Row, "A" & eqRow)
' If cell contents contain the word "subject" then do something (display message)
If InStr(UCase(subjCell.Value), "SUBJECT") > 0 Then
MsgBox "Subject: " & subjCell.Value & ", Unit: " & strItem
End If
Next subjCell
Else
MsgBox "Item not found: " & strItem & ".pdf"
End If
Next strItem
End With

Excel VBA .Find() misbehaving Cannot locate string

I have a series of values on a sheet
E11: "Notional"
E12: "Strike"
E13: "Coupon"
Etc.
In my code, use the sheet name
Function AddInput(name As String, strInput As String, Optional suffix = "") As String
Dim inputVal As Variant
On Error GoTo ERROR_FUNCTION
With Worksheets(name)
If .Cells.Find(what:=strInput, LookAt:=xlWhole,searchorder:=xlByRows).Offset(0, 1).Value <> "" Then
inputVal = Trim(Cells.Find(what:=strInput, LookAt:=xlWhole, searchorder:=xlByRows).Offset(0, 1).Value)
If TypeName(inputVal) = "Date" Then inputVal = Format(inputVal, "YYYYMMDD")
AddInput = Replace(strInput, " ", "") & "=" & inputVal & "&"
If suffix <> "" Then AddInput = AddInput & suffix
Else
AddInput = ""
End If
End With
Exit Function
ERROR_FUNCTION:
Debug.Print strInput & ": input not found"
MsgBox strInput & ": input not found"
End Function
I am able to find whats in Cell E12, but not E11.
I have done the following:
1) I copied the cell value into the search function directly (No chance to fat-finger it).
2) I copied the values from E11 down 1 (if for some reason it couldn't find that range etc... it just returned E12).
I still cannot find that one cell, it works for every other value I put through it.
Has anyone encountered this, and how did you resolve it?
Thanks!
Make sure you start each search correctly:
Sub dural()
Dim r As Range
strSheetname = ActiveSheet.Name
MyInputString = Application.InputBox(Prompt:="Enter value", Type:=2)
With Sheets(strSheetname)
Set r = .Cells.Find(what:=MyInputString, After:=Range("A1"))
End With
MsgBox r.Address(0, 0)
End Sub

Vba excel. Find text insie cell with line break

I have the following code. It searches one column for a specific value. It works fine, but if the cell have line break, the code does not search the second line.
vardestinolinha = ThisWorkbook.Sheets("base").Range("a11").End(xlDown).Row
a = 10
k = a
For i = a To vardestinolinha
Search = ThisWorkbook.Sheets(NomeTabela).Range("a2")
Replacement = ThisWorkbook.Sheets(NomeTabela).Range("c" & i)
varposicao = ThisWorkbook.Sheets(NomeTabela).Range("b" & i) '''''
Set rngFind = ThisWorkbook.Sheets("base").Columns(2).Find(What:=Search, LookIn:=xlValues, lookat:=xlPart)
Do While Not rngFind Is Nothing
tamanho = Len(rngFind)
p = InStr(1, rngFind, Search, vbTextCompare)
If p > 0 Then
ThisWorkbook.Sheets("base").Cells(k, 5) = ThisWorkbook.Sheets("base").Cells(k, 3)
k = k + 1
End If
Set rngFind = ThisWorkbook.Sheets("base").Columns(2).FindNext
Loop
k = i + 1
Next
I want the code to search an entire cell even when there are line breaks.
If the text was entered in the cell useing the Alt+Enter method you can use this in your VBA:
" & Chr(10) & "
Here is the .Find method that I have used.
Private Sub CommandButton1_Click()
Set RngClosedDate = Range("A1:Z10").Find(What:="Closed" & Chr(10) & "(Date)", LookAt:=xlWhole, LookIn:=xlValues)
' If the text that is searched for from the line above is not found then a message box is displayed and sub function is exitied
If RngClosedDate Is Nothing Then
MsgBox "Closed (Date) Column Header Not found. Cannot sort or format records."
Exit Sub
End If
End Sub