I am trying to check whether Row 1 of my active sheet named "Exceptions" contains the text "Control Date" (two spaces) or "Control Date".
My code finds the condition false.
Dim a As Range
Dim exceptions As Worksheet
Set exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each c In Exceptions.Range("A1:Z1")
If c = "Control Date" Then
Cells.Find(What:="control date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Else
Cells.Find(What:="Control Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
End If
Next c
Example of a worksheet with two spaces in "Control Date"
How to write the condition
As far as checking if the cell value is "Control Date" with a single space or one with two spaces, there are two ways of going about it:
Use the like operator
The like operator makes it easy to compare a string to a basic pattern. In this example, using the wildcard character * (Zero or more characters) will return true regardless of how many spaces are between Control and Date.
If cell.Value2 Like "Control*Date" Then
' Do something with that cell
End If
Use the or operator
Using the or operator ok to use as well, although not a flexible and perhaps a bit more difficult to see what's going on for your specific example.
If cell.Value2 = "Control Date" Or cell.Value2 = "Control Date" Then
' Do something with that cell
End If
Worksheet Codename
Each worksheet has whats called a codename. This is a reference that can be called directly in the code to that specific worksheet by it's name.
To set this name, in the properties window update the name property
So instead of
Dim Exceptions As Worksheet
Set Exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each cell In Exceptions.Range("A1:Z1")
' Do something...
Next cell
you can call the worksheet reference directly
For Each cell In Exceptions.Range("A1:Z1")
' Do something...
Next cell
Putting it together
Instead of using c for your variable, I like to make my variables easier to read and follow so I used cell.
Also, instead of hard coding your header columns in range, you could loop the cells of the entire first row. This is option suggestion though.
Lastly, be more explicit in what property you are looking for in your Cell. In my example I use .value2 to show I am looking for the value of that cell.
Public Sub Demo()
Dim cell As Range
For Each cell In Exceptions.Rows(1).Cells
If cell.Value2 Like "Control*Date" Then
' Do something with that cell
End If
Next cell
End Sub
Why duplicate the data into a third column? Whenever you need the "combined" date, just go get it, but do not store it twice.
Option Explicit '<<-- always have this
Sub doFindControl()
Dim a As Range
Dim c As Range '<<-- add this
Dim colDate As Long, colNumber As Long, colBlank As Long '<<--add this
Dim exceptions As Worksheet
Set exceptions = ActiveWorkbook.Worksheets("Exceptions")
For Each c In exceptions.Range("A1:Z1")
' first find the 2 key columns
If InStr(c, "Control") > 0 Then
If InStr(c, "Date") > 0 Then
colDate = c.Column
ElseIf InStr(c, "Number") > 0 Then
colNumber = c.Column
End If
' second look for the first blank column for you to put results in
ElseIf c.Text = "" Then
colBlank = c.Column
Exit For ' stop looking after its found
End If
Next c
' now you have the 2 FROM columns, and the TO column
MsgBox (colDate & " " & colNumber & " " & colBlank)
' and you can loop thru all the rest of the rows doing combine
End Sub
Thank you for all the answers! Robert Todar's answer led me to my lightbulb moment, and I can't believe at how simple the answer was. All I had to do was change this code:
Cells.Find(What:="Control Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
to:
Cells.Find(What:="Control*Date", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Related
Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 12 months ago.
Improve this question
I am trying to come up with a macro that will work on all sheets in my workbook. It needs to search for a specific "header" (there are 5 different headers that could be in a worksheet, but they won't all necessarily be there), and if it finds it, return a total that is within that section. However, the total is in a different column AND row than the header itself.
In the image below, there are two headers- "sales Commission" and "Sales Fee". Within each of those sections, there are dollar amounts, and a subtotal. I would like to run a macro that searches for Sales commission, and if it finds it, returns the total. Then it would look for "Sales Fee", and if it finds it, returns the total from that part of the sheet. If one of the headers is NOT present, it doesn't return anything.
Headers and values
Application.Match Instead of Find
Option Explicit
Sub GetTotalTEST()
Const Header As String = "Sales Fee"
Dim wb As Workbook: Set wb = ThisWorkbook ' workbook containing this code
Dim ws As Worksheet: Set ws = wb.Worksheets("Sheet1")
Dim Total As Double: Total = GetTotal(ws, Header)
Debug.Print Total
End Sub
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
' Purpose: In a column ('fCol') of a worksheet's ('ws') used range,
' it will try to find a string ('Header'). If found,
' in the column adjacent to the right, from the found cell's row
' towards the bottom, it will try to find another string
' ('stString'). If found, and if the cell adjacent to the right
' contains a number, it will return this number.
''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''''
Function GetTotal( _
ByVal ws As Worksheet, _
ByVal Header As String) _
As Double
Const fCol As Long = 1
Const stString As String = "Subtotal:"
With ws.UsedRange.Columns(fCol)
Dim fIndex As Variant: fIndex = Application.Match(Header, .Cells, 0)
If IsError(fIndex) Then Exit Function ' 'Header' not found
With .Resize(.Rows.Count - fIndex + 1).Offset(fIndex - 1, 1)
fIndex = Application.Match(stString, .Cells, 0)
If IsError(fIndex) Then Exit Function ' subtotal string not found
With .Cells(fIndex).Offset(, 1)
If IsNumeric(.Cells) Then GetTotal = .Value ' it is a number
End With
End With
End With
End Function
You can try this macro
enter exact header you want to find, then it gives out the subtotal.
Sub findheader()
Dim h As Integer
Dim s As String
s = InputBox("Enter header to find")
If Not Cells.Find(what:=s, After:=ActiveCell, LookIn:=xlFormulas2, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False) Is Nothing Then
Cells.Find(what:=s, After:=ActiveCell, LookIn:=xlFormulas2, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
h = 6
For i = 1 To h
If Not ActiveCell.Resize(i, i).Find(what:="subtotal", After:=ActiveCell, LookIn:=xlFormulas2, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False) Is Nothing Then
h = i
ActiveCell.Resize(i, i).Find(what:="subtotal", After:=ActiveCell, LookIn:=xlFormulas2, LookAt:=xlPart, SearchOrder:=xlByRows, SearchDirection:=xlNext, MatchCase:=False, SearchFormat:=False).Activate
MsgBox Application.TextJoin(" ", True, ActiveCell.Resize(1, 2))
End If
Next i
End If
End Sub
Notes:
Assumed your subtotal is within 6 cells square from your header. If your data is longer, you can put multiplier to longer your limit e.g. Resize(i, i * multiplier), or you can control h limit (currently 6 cells) h = 6.
Assumed your subtotal title, and subtotal value are next together, if not please change resize from subtotal title cell ActiveCell.Resize(1, 2)
I used record macro to create some code and then I put it in a loop. It works but there is an error in the find function which causes it to only work once. I tried to do something with the error but I am not having any luck having it loop. I've looked a couple of days here and there but I am at a loss. Hope you can help me. Much appreciated.
i = 1
On Error GoTo notfound
Do While Sheet1.Cells(i, 1) <> ""
Columns("J:J").Select
Selection.Find(What:="x", After:=ActiveCell, LookIn:=xlFormulas, _
LookAt:=xlPart, SearchOrder:=xlByColumns, SearchDirection:=xlNext, _
MatchCase:=False, SearchFormat:=False).Activate
Rows(ActiveCell.Row).EntireRow.Delete
notfound: msgbox "Finished"
GoTo notfound
Exit Sub
i = i + 1
Loop
I've corrected, completed, formatted and commented your code. This should take you one step closer to what you want to do.
Private Sub Sample()
Dim Crit As Variant ' the criterium to look for
Dim Fnd As Range ' the cell to find
Dim i As Long
' never create an error handler if you don't know which error to exect
' On Error GoTo notfound
i = 1
' the cell can't be "" only its value can do thjat
Do While Sheet1.Cells(i, 1).Value <> ""
Crit = "x"
' Columns("J:J").Select
' don't Select anything, address cells or ranges instead
Set Fnd = Columns("J:J").Find(What:=Crit, _
After:=ActiveCell, _
LookIn:=xlFormulas, _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
' where is the 'Activecell'?
' a) it doesn't change while this loop is running
' but it might get deleted by this loop's action
' b) it must be in the range you are searching
' that's why your code will fail most of the time.
' don't Activate anything. Instead address the object you want to deal with.
If Fnd Is Nothing Then
MsgBox "I didn't find """ & Crit & """"
Else
Sheet1.Rows(Fnd.Row).EntireRow.Delete
End If
i = i + 1
Loop
End Sub
This code will look for "x" in column J for as long as there is a value in column A and delete the row where it is found. It's hard to imagine a relationship between the number of entries in column A and the number of "x" in column J but, hopefully, this isn't your problem. Instead, your obvious problem is the cell in which you want to start the search. It definitely isn't ActiveCell but it might be Cells(1, "J"). You can also omit this instruction and VBA will start the search after J1.
You want to LookIn formulas. If there are formulas in column J the Formula will be different from the Value. You may wish to search in xlValues.
In Column L (only) I want to replace any instance of data with "True" regardless of what was originally in any of the Column L cells. The code I tried was:
With ActiveSheet
intLastRow = .Cells(.Rows.Count, "A").End(xlUp).Row
Let strSelectRange = "L2" & ":" & "L" & intLastRow
Range(strSelectRange).Select
Cells.Replace What:="*", Replacement:="True", LookAt:=xlPart _
, SearchOrder:=xlByColumns, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
End With
First, I used .Rows.Count, "A" because in that column every row has data so I know how many rows to go down in Column L. In Column L many cells will be blank.
When I run this, every cell in the entire worksheet that has anything in it, is changed to True, not just the data in Column L.
Another method I tried was:
Range("L2:L1200").Select
Selection.Replace What:="*", Replacement:="True", LookAt:=xlPart, _
SearchOrder:=xlByRows, MatchCase:=False, SearchFormat:=False, _
ReplaceFormat:=False
Range("A1").Select
What I don't like about this is that I picked L1200 as the number of rows just to be sure I'd search farther than the actual last row that can contain data. I'm worried that this method might cause some kind of problem at some point.
What I'd really like to know is what I'm doing wrong in the first code example.
Thanks for any help you can offer!!!
Search & Replace in Column
Use Option Explicit always, to quicker learn about the occurring
errors and to be forced to declare variables.
You should always declare your rows as Long.
When you use the With statement you use the dots on everything, even
on .Range and .Cells etc. The code might work in this case
(ActiveSheet) anyway, but it is incorrect.
Avoid the use of ActiveSheet, use the worksheet name.
Avoid the use of Select. There are many posts (articles) about this.
When ever you use Cells without anything behind it, it refers to all the
cells in the worksheet.
The first thing in the Replace function (Find function) is the range
where you're going to Replace (Find, Search). It can be a column, it
can be Cells or just a smaller range.
The Code
Sub SROneColumn()
Const cVntLRColumn As Variant = "A" ' Last Row Column Letter/Number
Const cVntCriteria As Variant = "L" ' Criteria Column Letter/Number
Const cLngFirstRow As Long = 2 ' First Row Number
Const cStrReplace As String = "True" ' Replace String
Dim lngLastRow As Long ' Last Row Number
Dim strSelectRange As String ' Select Range Address
With ActiveSheet
lngLastRow = .Cells(.Rows.Count, cVntLRColumn).End(xlUp).Row
strSelectRange = .Range(.Cells(cLngFirstRow, cVntCriteria), _
.Cells(lngLastRow, cVntCriteria)).Address
.Range(strSelectRange).Replace What:="*", Replacement:=cStrReplace, _
LookAt:=xlPart, SearchOrder:=xlByColumns, MatchCase:=False
End With
End Sub
An interesting way to use a worksheet without the use of an object variable:
Sub SRSheet()
Const cStrSheet As Variant = "Sheet1" ' Worksheet Name/Index
With ThisWorkbook.Worksheets(cStrSheet)
End With
End Sub
Range(strSelectRange).Select
selects a range (though best to avoid Select) but then your code does nothing with that selection because Cells is the entire sheet.
Maybe you want instead:
Range(strSelectRange).Replace What:="*", Replacement:="True", LookAt:=xlPart
I'm really new to VBA, and could do with your help please.
Sheet2 is a long list of data (jobs) where each row in column B contains a unique job reference number.
I want users to input one of these numbers into a cell in Sheet 1 (G11), then the macro searches Sheet2ColumnB for the number, goes across 21 cells in that same row, then enters today's date and time into that cell.
(It then goes back to Sheet1 and says "Job Booked Out" but I think I can do this bit)
I've tried to modify some other code I've found, but get errors in the 4th line, and I have no clue if it works.
Sub CloseJob()
Dim cell As Range
Dim temp As Range
For Each cell In Sheets("Sheet1").UsedRange.cell("G11").Cells
If cell <> "" And cell.Row <> 1 Then
Set temp = Sheets("Sheet2").Columns("B").Find(What:=cell.Value, _
LookIn:=xlFormulas, LookAt:=xlPart, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=True)
'if found
If Not temp Is Nothing Then
'if the search_criteria is in the same sheet
cell.Offset(0, 21) = Date
End If
End If
Next
End Sub
Error:
"Run-time error 483. Object doesn't support this property or method"
by your narrative you seem after this:
Sub CloseJob()
Dim temp As Range
Set temp = Sheets("Sheet2").Columns("B").Find(What:=Sheets("Sheet1").Range("G11").Value, _
LookIn:=xlFormulas, LookAt:=xlWhole, _
SearchOrder:=xlByRows, _
SearchDirection:=xlNext, MatchCase:=True)
'if found
If Not temp Is Nothing Then temp.Offset(0, 21) = Date
End Sub
notice I changed LookAt:=xlPart to LookAt:=xlWhole for an exact match
Test and I will be able to get connect with like minded peopl
I'm working on a database for my workplace, which is the Outreach department of a large science center. We have a lot of schools that violate our (pretty specific) contract, so we're looking to track and reduce these violations.
To cut down on misspellings that would make searching the database later more difficult, I want the user (when entering a new school) to pull up the Userform, enter the name of their school, then click a search button to populate a listbox with the names of schools that match what they entered. If they click on a school in that list, the form uses that as the school name. If not, it prompts them to enter a new school name.
My code is pretty basic right now. I'm trying to use a Find & FindNext procedure to pull up all instances of a school name, but I'm getting a Type Mismatch error (#13) with the code I have currently and I can't find where that might come from. I've checked that no variables or Userform objects are misspelled.
I want the Find function to return only the Range of the first cell, so that I can turn it into .Address or .Value as needed.
Option Explicit
Private Sub cbtnSearchSchool_Click()
Dim lrow As Long
Dim schoolmatch As Range
'defines "lrow" as the last completely empty row in the table
lrow = Cells.find(What:="", _
After:=Range("A1"), _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False).Row
Range("A1").Activate
'defines "schoolmatch" variable as the first school in the list that
'matches what was entered in the text box.
Set schoolmatch = Range("SchoolName").find(What:=txtbSchoolName.Value, _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
'returns value of first found cell to check for accuracy
MsgBox schoolmatch.Value
This is a photo of the Userform if anybody would like to see it.
Edit: Sorry, I actually dimensioned schoolmatch as a Range originally and just changed it to Object while I was debugging - I got the same error before and after changing it.
It shows me the line I'm getting the error on - it's the Set schoolmatch = Range.Find operation, but I can't figure out anywhere that I would be mixing up data types. I have a SchoolName range that I've double-checked, and I've checked all of the other variable names for misspellings.
Over time there will be thousands of schools on this list, so this search function is necessary to filter some results before users select a school on the form.
It looks like the use of Object may be causing the error. Also, with a slight tweak to how lRow is assigned, this should run better:
Option Explicit
Private Sub cbtnSearchSchool_Click()
Dim lrow As Long
Dim schoolmatch As Range
'defines "lrow" as the last completely empty row in the table
lrow = Range("A1").End(xlDown).Row
'defines "schoolmatch" variable as the first school in the list that
'matches what was entered in the text box.
Set schoolmatch = Range("SchoolName").find(What:=txtbSchoolName.Value, _
After:=ActiveCell, _
LookIn:=xlValues, _
LookAt:=xlPart, _
SearchOrder:=xlByColumns, _
SearchDirection:=xlNext, _
MatchCase:=False, _
SearchFormat:=False)
'returns value of first found cell to check for accuracy
MsgBox schoolmatch.Value
A note with lRow, if say cell A100 is empty but B100 isn't, it will improperly assign that row (row 100) as the last row.
If you really need to make sure this doesn't happen (same with your OP), you could loop through all rows until a blank. Like this perhaps:
lRow = 1
Do While WorksheetFunction.CountA(Rows(lRow)) <> 0
lRow = lRow + 1
Loop
Debug.print "Last row is: " & lRow