Excel VBA: If value not found in range, go to - vba

I am using the following code to loop through sheets 1-31 filtering each sheet by the value in a cell("E1") in sheets("RunFilter_2") and then copy the filtered range and copy to the next empty row in sheets("RunFilter_2").
The code errors when it doesn't find the value of sheets("RunFilter_2").Range("E1") in column 18 of the active sheet.
So I added a range check, that checks if sheets("RunFilter_2").Range("E1").Value is found in column Range("R:R").
But, how do I move to the Next I If rngFound Is Nothing?
Sub RunFilter2()
Rows("5:5").Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Delete Shift:=xlUp
Range("A1").Select
Dim wb As Workbook
Dim ws As Worksheet
Set wb = ActiveWorkbook
Set ws = wb.sheets("01")
Dim WS_Count As Integer
Dim I As Integer
WS_Count = ActiveWorkbook.Worksheets.Count - 3
For I = 1 To WS_Count
Dim LastRow As Long
LastRow = ActiveSheet.UsedRange.Rows.Count
sheets(I).Select
Columns("A:U").Select
Dim rng As Range
Dim rngFound As Range
Set rng = Range("R:R")
Set rngFound = rng.Find(sheets("RunFilter_2").Range("E1").Value)
If rngFound Is Nothing Then
'----------------------------------
' How do I code ... GO TO Next I
'----------------------------------
Else:
Selection.AutoFilter
ActiveSheet.Range("$A$1:$U" & LastRow).AutoFilter Field:=18, Criteria1:=sheets("RunFilter_2").Range("E1").Value
Range("A1").Offset(1, 0).Select
Rows(ActiveCell.Row).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
sheets("RunFilter_2").Select
If Range("A4").Value = "" Then
Range("A4").Select
Else
Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Select
End If
ActiveSheet.Paste
ws.Select
Application.CutCopyMode = False
Selection.AutoFilter
Range("A1").Select
sheets("RunFilter_2").Select
Next I
End Sub

You can do it like this:
For I = 1 To WS_Count
If rngFound Is Nothing Then goto NextIPlace
your code
NextIPlace:
Next I
But you should reconsider writing like this, it is not a good VBA practice to use GoTo. The whole code should be changed. Check more here. Once your code works, feel free to submit it at https://codereview.stackexchange.com/, they would give you good ideas.

There is no need to use GoTo here. The simple way to accomplish this is with the following:
For I = 1 To WS_Count
' do stuff
If Not rngFound is Nothing
'execute desired action
End If
' do more stuff
Next i
You can also place the do more stuff inside the first if block if needed. The code in your post was kind of messy and I didn't take time to dissect fully.

Place some label before Next I:
NextI:
Next I
Then you can do this:
If rngFound Is Nothing Then
Goto NextI
Else
....
Alternatively you can simplify it without needing the else statement
If rngFound Is Nothing Then Goto NextI
.... ' Proceed without the need for `Else` and `End If`
EDIT.. Some more
While it is generally considered bad programming practice to use Goto statements, it is not the case in this specific situation. It is just used as a workaround for the lack of the continue statement that exists in the C and derived languages.

you should add a marker before Next I
MARKER:
Next I
So after If rngFound Is Nothing Then you add GoTo MARKER

Related

VBA Range 1004 error

I am using the following code:
Sub CSVParser()
Dim i As Integer
Dim x As Integer
Dim values As Range
Sheets("CSV Paste").Select
Range("A3").Select
For i = 1 To Range("A3", Range("A3").End(xlDown)).Rows.Count
Range(Selection, Selection.End(xlToRight)).Select
Selection.Copy
Sheets("Working Sheet 1").Select
Range("A1").Select 'problem code
Do Until ActiveCell.Value = ""
If ActiveCell.Value = "" Then
Exit Do
Else
ActiveCell.Offset(1, 0).Select
End If
Loop
ActiveSheet.Paste
Sheets("CSV Paste").Select
ActiveCell.Offset(1, 0).Select
Next
End Sub
However, the line Range("A1").Select just after Sheets("Working Sheet 1").Select is kicking up a run-time error '1004'
Does anyone know why? I have rearranged this in every way I can think of an have typed it out from scratch again.
Give this version of your code a try:
Sub CSVParser()
Dim wb As Workbook
Dim wsCSV As Worksheet
Dim wsWork As Worksheet
Set wb = ActiveWorkbook
Set wsCSV = wb.Sheets("CSV Paste")
Set wsWork = wb.Sheets("Working Sheet 1")
wsCSV.Range("A3").CurrentRegion.Copy wsWork.Cells(wsWork.Cells.Count, "A").End(xlUp).Offset(1)
End Sub
Using .Select and .Activate is not considered 'best practice'. See How to avoid using Select in Excel VBA macros. Yes, using the code from the macro recorder is a good place to start but you have to get away from the practice at some point.
Performing bulk operations is preferred to looping through an indeterminate number of rows or columns.
Option Explicit
Sub CSVParser()
Dim lastCol As Long
With Worksheets("CSV Paste")
With .Range(.Cells(3, "A"), .Cells(.Rows.Count, "A").End(xlUp))
lastCol = .CurrentRegion.Columns.Count
With .Resize(.Rows.Count, lastCol)
.Copy Destination:=Sheets("Working Sheet 1").Range("A1")
End With
End With
End With
End Sub
I think this is what you are trying to achieve (without all the unnecessary Select):
Option Explicit
Sub CSVParser()
Dim i As Long
Dim x As Long
Dim LastRow As Long
Dim PasteRow As Long
With Sheets("CSV Paste")
LastRow = .Range("A3").End(xlDown).Row
For i = 3 To LastRow
PasteRow = Sheets("Working Sheet 1").Cells(Sheets("Working Sheet 1").Rows.Count, "A").End(xlUp).Row
.Range(.Range("A" & i), .Range("A" & i).End(xlToRight)).Copy Destination:=Sheets("Working Sheet 1").Range("A" & PasteRow + 1)
Next i
End With
End Sub

Copying dynamic rows from one worksheet to another in VBA

I am trying to copy a number of rows from each worksheet to the worksheet called "renew" in the same workbook.
The rows are defined as between the key words of "Service Requests" and "Renewals".
so step 1: define those row numbers, and step 2: copy them to the Renew sheet.
I run into the problem with step 2, somehow, i couldn't work out how to use the rownumber1 and rownumber2 in the copy command.
Any help would be appreciated. Thanks!
Sub test()
Dim ws As Worksheet
Application.ScreenUpdating = False
Sheets("Renew").Activate
For Each ws In Worksheets
If ws.Name <> "Renew" Then
For i = 1 To 100
Dim rownumber1 As Integer
Dim rownumber2 As Integer
If Range("A" & i).Text = "Service Requests" Then
rownumber1 = i
ElseIf Range("A" & i).Text = "Renewals" Then
rownumber2 = i
End If
Next i
'copy rows between rownumber1 and rownumber2 to the renew sheet
ws.Rows("rownumber1:rownumber2").EntireRow.Copy
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
End If
Next ws
End Sub
update:
Sub test2()
Dim ws As Worksheet
Dim rownumber1 As Integer
Dim rownumber2 As Integer
Dim FoundCell As Excel.Range
Application.ScreenUpdating = False
Sheets("Renew").Activate
For Each ws In Worksheets
If ws.Name <> "Renew" Then
Set FoundCell = ws.Range("A:A").Find(what:="Service Requests", lookat:=xlWhole)
If Not FoundCell Is Nothing Then
rownumber1 = FoundCell.Row
End If
Set FoundCell = ws.Range("A:A").Find(what:="Renewals", lookat:=xlWhole)
If Not FoundCell Is Nothing Then
rownumber2 = FoundCell.Row
End If
'copy renewals to the renewalsummary
ws.Rows(rownumber1 & ":" & rownumber2).EntireRow.Copy
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
End If
Next ws
End Sub
What you're looking for is:
ws.Rows(rownumber1 & ":" & rownumber2).EntireRow.Copy
Although, there are some other things to consider with your code. It may be a work in progress so I only answered your question, but:
Your loop is going to return row 100 every time, so I'm curious what the point of your loop is.
You should never DIM in a loop, since you can only declare a variable once and the loop will attempt to do it every time and should throw an error (Dim your rownumber variables with your ws variable).
Why loop to 100? You should loop to the end of the list of values.
Reply to Edits
It looks pretty good. the main thing is that it works. Although I would change this:
ActiveSheet.Paste Range("A65536").End(xlUp).Offset(1, 0)
to this:
ActiveSheet.Paste Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
Hard-coded values aren't very future-proof, this looks at the last row of the sheet (whatever that might be). If any of your sheets start to reach the row max you need to do this:
If Cells(Rows.Count, 1) <> "" Then
ActiveSheet.Paste Cells(Rows.Count, 1).End(xlUp).Offset(1, 0)
Else
MsgBox("Sheet " & ws.Name & " is full, row cannot be copied")
End If
But that's probably way down the road and at that point you might be outgrowing Excel.

Do Until loop need to restart on error

I have a Do Until loop in VBA.
My problem is that there is likely to be an error most days when running the macro as not all the sheets will have info on them.
When that happens I just want to start the loop again. I am assuming its not the "On Error Resume Next" I was thinking of counting the rows on the autofilter and then if it was 1 (ie only titles) starting the loop again. Just not sure how to do that.
Dim rngDates As Range 'range where date is pasted on.
'Dim strDate As String
Dim intNoOfRows As Integer
Dim rng As Range
Sub Dates()
Application.ScreenUpdating = False
Set rngWorksheetNames = Worksheets("info sheet").Range("a1")
dbleDate = Worksheets("front sheet").Range("f13")
Worksheets("info sheet").Activate
Range("a1").Activate
Do Until ActiveCell = ""
strSheet = ActiveCell
Set wsFiltering = Worksheets(strSheet)
intLastRow = wsFiltering.Cells(Rows.Count, "b").End(xlUp).Row
Set rngFilter = wsFiltering.Range("a1:a" & intLastRow)
With rngFilter
.AutoFilter Field:=1, Criteria1:="="
On Error Resume Next
Set rngDates = .Resize(.Rows.Count - 1, 1).Offset(1, 0).SpecialCells(xlCellTypeVisible)
End With
With rngDates
.Value = dbleDate
.NumberFormat = "dd/mm/yyyy"
If wsFiltering.FilterMode Then
wsFiltering.ShowAllData
End If
ActiveCell.Offset(1, 0).Select
End With
Application.ScreenUpdating = True
Worksheets("front sheet").Select
MsgBox ("Dates updated")
Loop
You could check existance of data after filtering by using SUBTOTAL formula.
If Application.WorkSheetFunction.Subtotal(103,ActiveSheet.Columns(1)) > 1 Then
'There is data
Else
'There is no data (just header row)
End If
You can read about SUBTOTAL here
Rather than using the Do Until loop, consider using a For Each loop on the Worksheets Collection.
ie.
Sub ForEachWorksheetExample()
Dim sht As Worksheet
'go to error handler if there is an error
On Error GoTo err
'loop through all the worksheets in this workbook
For Each sht In ThisWorkbook.Worksheets
'excute code if the sheet is not the summary page
'and if there is some data in the worksheet (CountA)
'(this may have to be adjusted if you have header rows)
If sht.Name <> "front sheet" And _
Application.WorksheetFunction.CountA(sht.Cells) > 0 Then
'do some stuff in here. Refer to sht as the current worksheet
End If
Next sht
Exit Sub
err:
MsgBox err.Description
End Sub
Also. I would recommend removing the On Error Resume Next statement. It is much better to deal detect and deal with errors rather than ignore them. It could cause strange results.

How do you do a loop that can go through an entire column in an excel workbook and search for all instances of a string?

Im an intern at a company where they do a lot of DCR and Inductance readings and have all the values on text files. Ive managed to use VBA to import those text files into an excel spreadsheet however now I need to start manipulating that data. I'm trying to write some code that will loop through an entire column and search for the string "**DCR" and then give me the data that is in the cell offset (1,3), copy, and then paste it to a different range within the same workbook. I've written code where is searches for the first instance of the string and then copies and pastes that data that I need into the range, but then it stops there. The Do Loop code that I wrote gives me an infinite loop and doesnt work. Here is my code so far.
Sub Button2_Click()
Dim rng1 As Range
Dim strSearch As String
strSearch = "**DCR"
Set rng1 = Range("A:A").Find(strSearch, , xlValues, xlWhole)
If Not rng1 Is Nothing Then
rng1.Offset(1, 3).Copy
Range("N11").Select
Selection.PasteSpecial Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks _
:=False, Transpose:=False
Range("O11").Select
Do
Set rng1 = Range("A:A").FindNext(rng1)
Loop
End If
End Sub
Can anyone tell me what I'm missing and/or doing wrong. Thank you very much!
Try this...
Sub Button2_Click()
Const DCR As String = "**DCR"
Dim rngSearch As Range
Set rngSearch = ActiveSheet.Range("A:A")
Dim rngFoundFirst As Range
Set rngFoundFirst = rngSearch.Find(DCR, LookIn:=xlValues, LookAt:=xlWhole, MatchCase:=True)
' Anything found?
If Not rngFoundFirst Is Nothing Then
Call ProcessDcr(rngFoundFirst)
Dim rngFoundNext As Range
Set rngFoundNext = rngFoundFirst
Do
Set rngFoundNext = rngSearch.FindNext(rngFoundNext)
' If first one is found, stop looping.
If Not rngFoundNext Is Nothing Then
If rngFoundNext.Address = rngFoundFirst.Address Then
Exit Do
End If
Call ProcessDcr(rngFoundNext)
End If
Loop Until rngFoundNext Is Nothing
End If
Set rngFoundNext = Nothing
Set rngFoundFirst = Nothing
Set rngSearch = Nothing
End Sub
Sub ProcessDcr(rngFound As Range)
Call rngFound.Offset(1, 3).Copy
Call Range("N11").PasteSpecial(Paste:=xlPasteValues, Operation:=xlNone, SkipBlanks:=False, Transpose:=False)
End Sub
You'll need to move the Do-Loop: basically loop as long as there are results to be found, quit when no result is found. But the code will still loop, cause findNext will continue to find the next result, even if it has already found it. So you have to keep to keep track of your first result.
You'll probably want to increment the location to which you copy.
Keep in mind that copy and paste is expensive in excel, a better and faster way is to copy the cell value.
Dim strSearch As String
Dim rng1 As Excel.Range
Dim firstrng1 As Excel.Range
Dim rowNumber as Integer
rowNumber = 11;
strSearch = "**DCR"
Set rng1 = Range("A:A").Find(strSearch, , xlValues, xlWhole)
If rng1 Is Nothing Then Exit Sub
Set firstrng1 = rng1
Do
Range("N" & rowNumber).Value = rng1.Offset(1, 3)
rowNumber = rowNumber + 1
Set rng1 = Range("A:A").FindNext(rng1)
If rng1.Address = firstrng1.Address Then Exit Do
Loop

VBA - Run macro in only one column

I have the code working exactly the way I'd like to, however I don't want it to skip onto another column. I just want my macro to run inside column C then exit.
I am new to VBA in excel, so please pardon my faults.
Any help would be much appreciated.
Thanks in advance.
Sub CopyValuetoRange()
'
' CopyValuetoRange Macro
Dim search_range As Range, Block As Range, last_cell As Range
Dim first_address$
Set search_range = ActiveSheet.UsedRange
Set Block = search_range.Find(what:="*", _
after:=search_range.SpecialCells(xlCellTypeLastCell), _
LookIn:=xlValues, searchorder:=xlColumns, searchdirection:=xlDown)
If Block Is Nothing Then Exit Sub
Set Block = Block.CurrentRegion
first_address$ = Block.Address
Do
Block.Select
Selection.End(xlDown).Select
ActiveCell.CurrentRegion.Rows(2).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.FormulaR1C1 = "=R[-1]C"
'MsgBox "Next Block Range"
Set last_cell = Block.Cells(Block.Rows.Count)
Set Block = search_range.FindNext(after:=last_cell).CurrentRegion
Loop Until Block.Address = first_address$ 'ActiveSheet.Range("C26").End(xlDown).Row
End Sub
Here is something I modified from something I found that will essentially do the same thing, however it puts the first cells value into all cells in the range. And this macro actually stays in Column C, since I found recently because it's not a region, it's a range.
Is there a way to change the following to add a formula to all cells in the range that points to the first cell in the range?
Sub Macro5()
Dim Rng As Range
Dim RngEnd As Range
Dim rngArea As Range
Set Rng = Range("C1")
Set RngEnd = Cells(Rows.Count, Rng.Column).End(xlDown)
If RngEnd.Row < Rng.Row Then Exit Sub
Set Rng = Range(Rng, RngEnd)
On Error GoTo ExitSub
Set Rng = Rng.SpecialCells(xlCellTypeConstants)
For Each rngArea In Rng.Areas
rngArea.Value = rngArea.Cells(Rng.Rows.Count, 1).Value
Next rngArea
ExitSub:
' Macro will exit here if the range is empty.
End Sub
How about you change your search_range, so that you only search Column C?
Set search_range = ActiveSheet.Range("C:C")
Set Block = search_range.Find(what:="*", _
LookIn:=xlValues, searchorder:=xlColumns, searchdirection:=xlDown)
Here's what I have, it's not pretty but it works. I added a column on both sides then removed them after the macro went through the entire column:
Sub CopyFirstCellInRangeInOneColumn()
'
' CopyValuetoRange Macro
Dim search_range As Range, Block As Range, last_cell As Range
Dim first_address$
''
Columns("C:C").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
Columns("E:E").Select
Selection.Insert Shift:=xlToRight, CopyOrigin:=xlFormatFromLeftOrAbove
''
Set search_range = ActiveSheet.Range("D:D")
Set Block = search_range.Find(what:="*", _
LookIn:=xlValues, searchorder:=xlColumns, searchdirection:=xlDown)
'Set search_range = ActiveSheet.UsedRange
'Set Block = search_range.Find(What:="*", _
' After:=search_range.SpecialCells(xlCellTypeLastCell), _
' LookIn:=xlValues, SearchOrder:=xlColumns, SearchDirection:=xlDown)
If Block Is Nothing Then Exit Sub
Set Block = Block.CurrentRegion
first_address$ = Block.Address
Do
Block.Select
Selection.End(xlDown).Select
ActiveCell.CurrentRegion.Rows(2).Select
Range(Selection, Selection.End(xlDown)).Select
Selection.FormulaR1C1 = "=R[-1]C"
MsgBox "Next Block Range"
Set last_cell = Block.Cells(Block.Rows.Count)
Set Block = search_range.FindNext(After:=last_cell).CurrentRegion
Loop Until Block.Address = first_address$ 'ActiveSheet.Range("C26").End(xlDown).Row
Columns("C:C").Select
Selection.Delete Shift:=xlToLeft
Columns("D:D").Select
Selection.Delete Shift:=xlToLeft
End Sub