VBA rejecting empty Range of SpecialCells - vba

I'm having a problem with a "For Each" related to a Range defined by a SpecialCells method. When I run the code below, there is a "Next without For" error, which I believe is because the rRange is empty when I first run the code. I could put "On Error Resume Next" in the beggining of the sub, but I'm trying to avoid this.
Public Sub Sub1()
Set rRange = Worksheets("Combate").Range("69:99").SpecialCells(xlCellTypeConstants, xlNumbers)
If Not rRange Is Nothing Then
For Each c In rRange
If c.Value <= turnoseg Then
c.Offset(-2 * lincomb0 + 6).Value = c.Offset(-lincomb0 + 3).Value
c.Value = ""
Next c
atualizarefeitos6
End If
End Sub
In another sub, I'm having a "No cells were selected" error after I run the code below. I really don't know how to actually solve the errors in these subs, but you guys surely would know.
Sub efeitosaddatac6()
'On Error Resume Next
Set rRange = Worksheets("Combate").Range("69:99").SpecialCells(xlCellTypeConstants, xlNumbers)
For Each c In rRange
c.Value = c.Value + 1
Next c
atualizarefeitos6
End Sub
Thanks in advance.

As pointed out in a comment by John Coleman, your first subroutine isn't working because you are missing an End If. You probably want:
Public Sub Sub1()
Set rRange = Worksheets("Combate").Range("69:99").SpecialCells(xlCellTypeConstants, xlNumbers)
If Not rRange Is Nothing Then
For Each c In rRange
If c.Value <= turnoseg Then
c.Offset(-2 * lincomb0 + 6).Value = c.Offset(-lincomb0 + 3).Value
c.Value = ""
End If
Next c
atualizarefeitos6
End If
End Sub
This is one of the reasons that consistent indentation of code is useful - it highlights missing End Ifs, etc.
I would recommend you change your second subroutine as follows:
Sub efeitosaddatac6()
Set rRange = Nothing
On Error Resume Next
Set rRange = Worksheets("Combate").Range("69:99").SpecialCells(xlCellTypeConstants, xlNumbers)
On Error GoTo 0
If Not rRange Is Nothing Then
For Each c In rRange
c.Value = c.Value + 1
Next c
atualizarefeitos6
End If
End Sub
Also, if you are not already using Option Explicit at the start of your code module, I recommend you do so. (I'm hoping that you are already using it, and that the lack of variable declarations within each subroutine is simply because they have all been declared at the module level.)

Luis Filho,
You need to insert:
End If
before
Next c
Another item you need to define is:
atualizarefeitos6
Is this a variable or function?

Related

Search in Excel loops to infinity, why?

I have a table with two data columns. I need to find the line where both of them are a hit. So far this is what my code looks like:
Dim ws As Worksheet
Set ws = Worksheets(1)
Set rgfound = ws.Range("A:A").Find(MyInputOne.value), LookAt:=xlWhole)
If rgfound Is Nothing Then
MsgBox "No results"
Exit Sub
Else
If rgfound.Offset(0, 3).Value <> MyInputTwo.Value Then
Do
Set rgfound = ws.Range("A:A").FindNext(rgfound)
Loop Until rgfound.Offset(0, 3).Value = MyInputTwo.Value
End If
End If
rgfound.Offset(0, 5).Value = "Found!"
This enters an infinite loop and crashes, and I have to force quit excel after running it.
Any advice will be appreciated.
I wouldn't use loops for this kind of thing.
The built in AutoFilter is super quick if you know how to manipulate it in VBA and it will avoid any need for loops/ problems with infinite loops
Here's the code:
Option Explicit
Sub FindBoth()
Dim sht As Worksheet
Dim data As Range, result As Range
Set sht = ThisWorkbook.Worksheets("Sheet1")
Set data = sht.Range("A1:D101")
sht.AutoFilterMode = False 'clear existing filter
With data
.AutoFilter field:=1, Criteria1:=8
.AutoFilter field:=4, Criteria1:="A"
Set result = .Offset(1, 0).SpecialCells(xlCellTypeVisible).Rows(1)
If Not Intersect(result, data) Is Nothing Then
result.Offset(0, 5).Resize(1, 1).Value = "Found!"
End If
End With
sht.AutoFilterMode = False
End Sub
My data looked like this:
Most probably it loops to infinity, because the code never satisfies this condition:
Loop Until rgfound.Offset(0, 3).Value = MyInputTwo.Value
To see what is happening write:
Debug.Print rgfound.Offset(0, 3).Value
Debug.Print MyInputTwo.Value
before the condition above. Another option to check what is happening is to introduce some counter, which would force the code to stop after the 2000. iteration. Like this:
Public Sub TestMe()
Dim cnt As Long
Do
cnt = cnt + 1
Debug.Assert cnt < 2000
Debug.Print cnt
Loop Until False
End Sub
Once it stops because of the fail of the Debug.Assert condition, you would be able to debug manually and to get what is happening.
Definitely the most simplistic answer here, but if you know your parameters and it should never 'Loop' over a certain amount of times, you can try putting a FOR I / NEXT I statement.
Just another idea to throw out there.

Hiding Empty Cells

I'm currently working on a code that hides empty cells ,but the problem is i want it to start hiding at a certain range ("A9:A12") not at the beginning of the sheet.
here is my program :
Sub EmptyRow()
'Dim s As String
po = Range("A9:A12").Count
Range("A8").Activate
For i = 1 To po
s = i & ":" & i
If IsEmpty(Cells(i, 1).Value) Then
Rows(s).Select
Selection.EntireRow.Hidden = True
End If
Next
End Sub
The program keeps on hiding cells from the beginning, how do I set it up so it deletes from the range i want it to. Please help.
You can even make your code shorter like this:
For i = 9 To 12
Cells(i, 1).EntireRow.Hidden = IsEmpty(Cells(i, 1).Value)
Next i
Thus, the result of the Hidden property would be dependent on whether the Cells(i,1) is empty. It is easier to understand and to maintain.
Check the solution below. In case you need to change your affected area, just change the value of targetRange.
Sub EmptyRow()
Dim targetRange as Range, po as Long, i as Long
Set targetRange = Range("A9:A12")
po = targetRange.Count
With targetRange
For i = 1 To po
If IsEmpty(.Cells(i, 1).Value) Then
.Rows(i).EntireRow.Hidden = True
End If
Next
End With
End Sub
Sheets("Sheet1").Range("A9:A12").SpecialCells(xlCellTypeBlanks).EntireRow.Hidden = True
SpecialCells results in run-time error if no cells are found, but that can be checked:
If [CountBlank(Sheet1!A9:A12)] Then _
[Sheet1!A9:A12].SpecialCells(xlCellTypeBlanks).EntireRow.Hidden = True
or ignored:
On Error Resume Next
[Sheet1!A9:A12].SpecialCells(xlCellTypeBlanks).EntireRow.Hidden = True
You can get rid of bits like select
Sub EmptyRow()
For i = 9 To 12
If IsEmpty(Cells(i, 1).Value) Then
Cells(i, 1).EntireRow.Hidden = True
End If
Next i
End Sub

How to avoid need to activate worksheet every loop

I've set up some VBA code in Excel that asks the user to select a second worksheet, then searches it for a value (a shared key linking the two sets of data, found 6 columns after Rng, where I want to add the retrieved value) in the second table and adds a value from that row to a column in the original table. The part of the program that I would like to adjust is the loop below.
It works fine if when I leave in the line to activate the CurFile workbook. But it means my screen is flashing a lot back and forth between the two workbooks. And once I start getting into hundreds or thousands of lines of data it will be ridiculously slow.
When I comment out that line, the value for FindCID doesn't change and it seems to just keep on refilling the same line, even though the value for r is updating. If after a few loops I add the activate line back in, it resumes properly filling in the results several lines down.
How can I streamline this? I originally was using ThisWorkbook references but even with explicitly defining CurFile (CurFile = ActiveWorkbook.Name) earlier it doesn't seem to go back to that workbook to look up the next value to search for, unless I reactivate the sheet.
Do While r <= maxRows
With Workbooks(CurFile).Worksheets("Sheet1")
Set Rng = .Range(Cells(r, c), Cells(r, c))
End With
FindCID = Rng.Offset(0, 6).Value
If Trim(FindCID) <> "" Then
With Workbooks(FN) ' found earlier by a function
.Activate
End With
With Sheets("Sheet1").Range("D:D")
Set FoundCell = .Find(What:=FindCID)
If Not FoundCell Is Nothing Then
PathLen = FoundCell.Offset(0, 2).Value
Workbooks(CurFile).Sheets("Sheet1").Activate 'If I comment out this line it doesn't work
Rng.Value = PathLen
MsgBox "CID found in " & FoundCell.Address & " Its value is " & PathLen
Else
MsgBox "Nothing found"
End If
End With
End If
On Error Resume Next
r = r + 1
Loop
Actually when working with objects, in most of the cases, there is no need to activate the workbooks\worksheets.
This is your code with some modifications in this regard:
Application.ScreenUpdating = False '(as suggested by CBRF23)
'......
'begining of your code
'......
Do While r <= maxRows
With Workbooks(CurFile).Worksheets("Sheet1")
Set Rng = .Cells(r, c) '(1)
End With
FindCID = Rng.Offset(0, 6).Value2
If Trim(FindCID) <> "" Then
Set FoundCell = Workbooks(FN).Sheets("Sheet1").Range("D:D").Find(What:=FindCID)
If Not FoundCell Is Nothing Then Rng.Value = FoundCell.Offset(0, 2).Value2
End If
r = r + 1
Loop
'......
'rest of your code
'......
Application.ScreenUpdating = True
(1) Notice that way the Range is defined as it’s made of only once Cell; but if the range has more than one Cell i.e. from Cell(r,c) to Cell(r,c+5) then you need to use the form:
Set Rng = Range(.Cells(r, c), .Cells(r, c+5))
There is no need to add a period . before Range as the range is defined by the Cells within the Range command. By using the period . before the Cell command they are referred as part of the
With Workbooks(CurFile).Worksheets("Sheet1")
However if the Range is defined as A1:F1 then the period . has to be added before the Range as in:
Set Rng = .Range(“A1:F1”)
I removed the MsgBox commands as I believe they were just for testing purposes. Not really showing these messages for hundreds or thousands lines of data. Isn’t it?

Excel autofilter errors

I am trying to autofilter on any cell with #DIV/0 using the code below however it keeps returning "Some Other Error" even though there are the above errors in column A.
Sub asdf2()
Dim R As Range
Set R = Range("A:A")
If IsError(R.Value) = True Then
If R.Value = CVErr(xlErrDiv0) Then
With R
.AutoFilter field:=1, Criteria1:=R.Value
End With
End If
Else
Debug.Print "Some other error"
End If
End Sub
As R is a multi cell range spanning a whole column, there is no .Value property.
Debug until you executed the Set R = ... statement and then examine the properties of R in the Locals window.
Secondly - interpreting your Else / Debug.Print branch, you are looking only for cells containing any error. Your Else branch will also be executed for cells not containing any error. So your Else branch should be attached to the inner If
Lastly, you cannot provide an error value as an argument for .Autofilter ... use a string Criteria1:="#DIV/0!" instead.
Mocking this all up ...
Sub asdf2()
Dim R As Range, C As Range
Set R = Range("A:A")
For Each C In R.Cells
Debug.Print C.Value
If IsError(C.Value) = True Then
If C.Value = CVErr(xlErrDiv0) Then
With R
.AutoFilter field:=1, Criteria1:="#DIV/0!"
End With
Else
Debug.Print "Other Error"
End If
Exit For
End If
Next C
End Sub
So here
on detecting a #DIV/0! error an autofilter is set
on detecting any other error a debug.print is issued
otherwise nothing happens
in case of 1,2 the loop is terminated immediately - no further checking

Selecting/deleting certain rows depending on value

I wrote this script to delete rows which contain a value in column C that is different than "201103". When I use this to bold it, it works, but when I use it with .Delete it behaves strange and does not work properly.
I was trying to get selected rows and than use UNION to merge it and use .SELECT (multiple) so I could delete it manually but not sure how to make it.
Sub test()
Dim Cell As Range
For Each Cell In Range("C2:C2308").Cells
If (Cell.Value <> "201103" And Cell.Value <> "") Then
Cell.EntireRow.Font.Bold = True
'Cell.EntireRow.Delete
End If
Next Cell
End Sub
Does anyone know how to fix it so it works fine?
Try this:
Sub test()
'
With ActiveSheet
.AutoFilterMode = False
With Range("C2", Range("C" & Rows.Count).End(xlUp))
.AutoFilter 1, "<>201103"
On Error Resume Next
.Offset(1).SpecialCells(12).EntireRow.Delete
End With
.AutoFilterMode = False
End With
End Sub