Use autofilter to copy visible cells to variable - excel

I'm using AutoFilter to delete some rows before a different macro is run. One of my filters brings up some notes that I need to save for later pasting. I assume saving it to a variable will allow me to then delete those rows and paste them when needed later. Here's what I have so far:
Sub Test()
With Sheet1
.AutoFilterMode = False
With Range("A10", Range("A" & Rows.Count).End(xlUp))
On Error Resume Next
.AUTOFILTER Field:=1, Criteria1:="=(**", Operator:=xlFilterValues
'Insert code to copy values to variable here
.Offset(1, 0).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.AutoFilterMode = False
End With
End Sub
I can't figure out how to copy the visible cells to a variable. Tried using xlCellTypeVisible but it didn't work.

So I figured it out. Here's the code below
With Sheet1
.AutoFilterMode = False
With Range("A10", Range("A" & Rows.Count).End(xlUp))
On Error Resume Next
.AUTOFILTER Field:=1, Criteria1:="=Device*", Operator:=xlFilterValues
.Offset(1, 0).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible).EntireRow.delete
.AUTOFILTER Field:=1, Criteria1:="=Manufacturer", Operator:=xlFilterValues
.Offset(1, 0).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible).EntireRow.delete
.AUTOFILTER Field:=1, Criteria1:="=(**", Operator:=xlFilterValues
Set MyRange = Sheet1.Range("A10: I" & Cells(Rows.Count - 1, 1).End(xlUp).Row).Offset(1, 0).SpecialCells(xlCellTypeVisible)
CalVal = MyRange
.Offset(1, 0).Resize(.Rows.Count - 1, 1).SpecialCells(xlCellTypeVisible).EntireRow.delete
End With
.AutoFilterMode = False
End With
This deletes the rows I needed deleted while saving the ones that start with "(".
Later I paste it on sheet 2 after everything else has been copied.
'Paste Notes
NumRows = UBound(CalVal, 1) - LBound(CalVal, 1) + 1
NumCols = UBound(CalVal, 2) - LBound(CalVal, 2) + 1
Set Destination = ws2.Range("A" & lastRow).Offset(1, 0)
Destination.Value = "Table Notes"
Destination.Font.Bold = True
Set Destination = ws2.Range("A" & lastRow).Resize(NumRows, NumCols).Offset(2, 0)
Destination = CalVal
Need to Dim MyRange, CalVal, LastRow, NumRows, and NumCols of course

Related

Auto filtering to exclude specific month

I have a loop that is deleting rows based on the month within the date
Dim k As Long
For k = FindLastRow(.Sheets(NewSheet)) To 2 Step -1
If Not Month(.Sheets(NewSheet).Cells(k, 1).Value) = NewMonth Then
.Sheets(NewSheet).Rows(k).EntireRow.Delete
End If
Next k
This is very slow and I have code that I've used elsewhere for doing this quicker, this example is based on deleting 0 values:
Dim rngDataBlock As Range
Set rngDataBlock = .Range(.Cells(1, 1), .Cells(8, 8))
With rngDataBlock
.AutoFilter Field:=1, Criteria1:=0
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Rows.Delete
End With
.AutoFilterMode = False
What I can't figure out is how to apply this to my 1st case where I'm deleting based on the month of the date. I tried:
.AutoFilter Field:=1, Criteria1:="<>" & Month(NewMonth)
but this doesn't work, I guess as the filter is actually loking at whole dates rather than months. Can anyone help?
You can use the second criteria and operator parameters to delete using autofilter.
Dim rngDataBlock As Range
Set rngDataBlock = .Range(.Cells(1, 1), .Cells(16, 2))
With rngDataBlock
.AutoFilter Field:=2, Criteria1:=">=" & DateSerial(2021, Month(newmonth), 1), _
Operator:=xlAnd, Criteria2:="<=" & DateSerial(2021, Month(newmonth) + 1, -1)
.Offset(1, 0).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible).Rows.Delete
End With
.AutoFilterMode = False
You can also speed up your original example by unionizing the rows you want to delete and then deleting in one go.
Dim k As Long
Dim delrng As Range
For k = FindLastRow(.Sheets(NewSheet)) To 2 Step -1
If Not Month(.Sheets(NewSheet).Cells(k, 1).Value) = newmonth Then
If delrng Is Nothing Then
Set delrng = .Sheets(NewSheet).Rows(k).EntireRow
Else
Set delrng = Union(delrng, .Sheets(NewSheet).Rows(k).EntireRow)
End If
End If
Next k
delrng.Delete

Rows are not getting deleted

The below code is not deleting the rows as expected. Can someone tell me what is wrong with it?
With Worksheets("Alerts Ack By Cops")
For rw = .Cells(.Rows.Count, "A").End(xlUp).Row To 2 Step -1
Select Case UCase(.Cells(rw, "A").Value2)
Case "Punna,", "Juleas,"
.Rows(rw).EntireRow.Delete
i = i - 1
End Select
Next rw
End With
When you are looking for text with capital letter using UCase then you need to match with upper case values.
Change
Case "Punna,", "Juleas,"
to
Case "PUNNA,", "JULEAS,"
Also since you want to match the cell which starts with those words, you can use LIKE as shown below
With Worksheets("Alerts Ack By Cops")
For rw = .Cells(.Rows.Count, "A").End(xlUp).Row To 2 Step -1
If UCase(.Cells(rw, "A").Value2) Like "PUNNA,*" Or _
UCase(.Cells(rw, "A").Value2) Like "JULEAS,*" Then
.Rows(rw).EntireRow.Delete
End If
Next rw
End With
Also, using autofilter would be faster. You may want to see Delete row based on partial text. Here the cirteria becomes "=" & strSearch & "*" instead of "=*" & strSearch & "*"
If you still want to delete the rows using a loop then you may want to see Union way of doing it in Excel VBA - Delete empty rows which will increase the speed drastically over a large number of rows.
Delete Rows Using AutoFilter
The first solution illustrates the use of two criteria.
The second solution illustrates how to do the same with an array. Unfortunately AutoFilter can have only two criteria containing wild characters. If you add more, nothing will be filtered.
The third solution illustrates a workaround which allows more than two criteria containing wild characters.
The Code
Option Explicit
Sub deleteTwoCriteria()
Application.ScreenUpdating = False
With Worksheets("Alerts Ack By Cops")
.AutoFilterMode = False
With .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
.AutoFilter _
Field:=1, _
Criteria1:="Punna,*", _
Operator:=xlOr, _
Criteria2:="Juleas,*"
With .Resize(.Rows.Count - 1).Offset(1)
On Error Resume Next
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
On Error GoTo 0
End With
End With
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
End Sub
Sub deleteArray()
Application.ScreenUpdating = False
With Worksheets("Alerts Ack By Cops")
.AutoFilterMode = False
With .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
.AutoFilter _
Field:=1, _
Criteria1:=Array("Punna,*", "Juleas,*"), _
Operator:=xlFilterValues
With .Resize(.Rows.Count - 1).Offset(1)
On Error Resume Next
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
On Error GoTo 0
End With
End With
.AutoFilterMode = False
End With
Application.ScreenUpdating = True
End Sub
Sub deleteList()
Const CriteriaList As String = "Punna,*|Juleas,*|R2*"
Dim Criteria() As String: Criteria = Split(CriteriaList, "|")
Application.ScreenUpdating = False
With Worksheets("Alerts Ack By Cops")
.AutoFilterMode = False
With .Range("A1", .Cells(.Rows.Count, "A").End(xlUp))
If .Cells.Count > 1 Then
Dim Data As Variant: Data = .Value
Dim dict As Object
Set dict = CreateObject("Scripting.Dictionary")
dict.CompareMode = vbTextCompare
Dim i As Long
Dim n As Long
For i = 2 To UBound(Data, 1)
For n = 0 To UBound(Criteria)
If LCase(Data(i, 1)) Like LCase(Criteria(n)) Then
dict(Data(i, 1)) = Empty
Exit For
End If
Next n
Next i
If dict.Count > 0 Then
.AutoFilter _
Field:=1, _
Criteria1:=dict.Keys, _
Operator:=xlFilterValues
With .Resize(.Rows.Count - 1).Offset(1)
.SpecialCells(xlCellTypeVisible).EntireRow.Delete
End With
.Worksheet.AutoFilterMode = False
End If
End If
End With
End With
Application.ScreenUpdating = True
End Sub

VBA: Looping a condition through a range that compares values from other columns until the list ends

Public Sub MainTOfomat()
Dim ShippingQty As Range
Dim ReceivedQty As Range
ActiveSheet.Columns("A:P").AutoFit
ActiveSheet.Range("A:P").AutoFilter Field:=13, Criteria1:="No"
ActiveSheet.Range("K:L").AutoFilter Field:=2, Criteria1:="<>"
Set ShippingQty = Range("K2")
Set ReceivedQty = ShippingQty.Offset(0, 1)
ShippingQty.Select
Do Until IsEmpty(ActiveCell)
If ShippingQty.Value = 0 Then
ShippingQty.Offset(0, 5) = "Needs Fulfillment"
ElseIf ShippingQty.Value > ReceivedQty.Value Then
ShippingQty.Offset(0, 5) = "Needs Receipt"
End If
ActiveCell.Offset(1, 0).Select
Loop
End Sub
The code is program is supposed to loop though each row in the column and fill in the statement based on the result of the condition for values in two other columns. The problem is that the loop goes through, but only the first line actually changes, and the auto filter code before the loop gets skipped.
Here is your macro fixed up.
As mentioned before your ShippingQty range and ReceivedQty do not change with the activecell. When moving to the next cell, that is the activecell. The filter range need to be the same. A:P is filtered, when changing to K:L ,field 2 actually becomes column B, so if you want to filter out non-blanks in column L you need the field 12.
Sub YourMacro()
Dim ShippingQty As Range
Dim ReceivedQty As Range
ActiveSheet.Columns("A:P").AutoFit
With ActiveSheet.Range("A:P")
.AutoFilter Field:=13, Criteria1:="No"
.AutoFilter Field:=12, Criteria1:="<>"
End With
Set ShippingQty = Range("K2")
Set ReceivedQty = ShippingQty.Offset(0, 1)
ShippingQty.Select
Do Until IsEmpty(ActiveCell)
If ActiveCell.Rows.Hidden = False Then
If ActiveCell.Value = 0 Then
ActiveCell.Offset(0, 5) = "Needs Fulfillment"
ElseIf ActiveCell.Value > ActiveCell.Offset(, 1).Value Then
ActiveCell.Offset(0, 5) = "Needs Receipt"
End If
End If
ActiveCell.Offset(1, 0).Select
Loop
ActiveSheet.AutoFilterMode = 0
End Sub
You can use this option as well without using selects.
Sub Option1()
Dim rng As Range, c As Range
Dim ws As Worksheet
Set ws = ActiveSheet
Application.ScreenUpdating = 0
With ws
Set rng = .Range("K2:K" & .Cells(.Rows.Count, "K").End(xlUp).Row)
With .Range("A:P")
.AutoFilter Field:=13, Criteria1:="No"
.AutoFilter Field:=12, Criteria1:="<>"
End With
For Each c In rng.SpecialCells(xlCellTypeVisible)
If c = 0 Then c.Offset(, 5) = "Needs Fulfillments"
If c > c.Offset(, 1) Then c.Offset(, 5) = "Needs Receipts"
Next c
.AutoFilterMode = False
End With
End Sub

VBA: For loop that does not want to iterate more than once

I have written a simple code to delete the lines of my Excel worksheet where the value is not numerical and where there is no value. But, weirdly enough, the loop executes only once when I run the program.
How can I solve this so that my program deletes all at once the lines that meet my two conditions?
Sub foo()
Dim lRow As Integer
Dim sht As Worksheet
Set sht = ActiveWorkbook.ActiveSheet
lRow = sht.Range("A" & Rows.Count).End(xlUp).Row
For Each c In Range(sht.Cells(2, 1), sht.Cells(lRow, 1))
If (Not IsNumeric(c.Value) Or c.Value = "") Then c.EntireRow.Delete
Next
End Sub
This uses AutoFilter to remove targeted rows in 2 steps:
Criteria1:="=*" shows non-empty strings, Criteria2:="=" shows empty values
Option Explicit
Public Sub foo()
Application.ScreenUpdating = False
With ActiveWorkbook.ActiveSheet.UsedRange
'Step 1 - Remove all strings and empty values:
.AutoFilter field:=1, Criteria1:="=*", Operator:=xlOr, Criteria2:="="
.Offset(1).Resize(.Rows.Count - 1).EntireRow.Delete 'Excludes the header row
'Step 2 - Remove all numbers that are not 6 digits in length:
.AutoFilter field:=1, Criteria1:="<100000", Operator:=xlOr, Criteria2:=">999999"
.Offset(1).Resize(.Rows.Count - 1).EntireRow.Delete 'Excludes the header row
.AutoFilter 'Removes filter
End With
Application.ScreenUpdating = True
End Sub
Edit:
The above version will exclude the header row, which becomes the AutoFilter row (with the arrow)
If there is no header row there are more checks to be done.
For example with this data:
100,000
100,001
100,003
The first visible cell (not included in the filter) will be 100,000 which shouldn't be deleted
If the data is:
Abc
100,000
100,001
100,003
The first visible cell (not included in the filter) will be Abc which should be deleted
So version 2 (bellow) addresses this issue:
Option Explicit
Public Sub foo()
Dim rowsToDelete As Range
Application.ScreenUpdating = False
With ActiveWorkbook.ActiveSheet.UsedRange
'Step 1 - Remove all strings and empty values:
.AutoFilter Field:=1, Criteria1:="=*", Operator:=xlOr, Criteria2:="="
Set rowsToDelete = CheckFirstCell(.Columns(1))
If Not rowsToDelete Is Nothing Then rowsToDelete.EntireRow.Delete
'Step 2 - Remove all numbers that are not 6 digits in length:
.AutoFilter Field:=1, Criteria1:="<100000", Operator:=xlOr, Criteria2:=">999999"
Set rowsToDelete = CheckFirstCell(.Columns(1))
If Not rowsToDelete Is Nothing Then rowsToDelete.EntireRow.Delete
.AutoFilter 'Removes filter
End With
Application.ScreenUpdating = True
End Sub
Private Function CheckFirstCell(ByRef rng As Range) As Range 'It can return Nothing
If Not rng Is Nothing Then
Dim tmp As Variant
With rng
.SpecialCells(xlVisible).Select
tmp = Selection(1).Value2
If Not IsNumeric(tmp) Or (tmp < 100000 Or tmp > 999999) Or Len(tmp) = 0 Then
Set CheckFirstCell = .EntireRow
End If
If Selection.Count > 1 Then
If CheckFirstCell Is Nothing Then
Set CheckFirstCell = .Offset(1).Resize(.Rows.Count - 1).EntireRow
Else
Set CheckFirstCell = .EntireRow
End If
End If
.Cells(1).Select
End With
End If
End Function

Auto filter to select just the visible rows

I have this code. It loops through a list for the filtering criteria, then if no data to select it shows all data again and loops to the next criteria. If it shows data it end(slDown) and selects all the data showing, copies it and pastes it into another worksheet.
The cleanup script cleans any blank rows and columns and then returns to the original data sheet and deletes the data selected for the copy paste.
The problem is when there is just one row. It moves to the row with data, but when I End(xlDown), it shoots all the way to the bottom and the paste then causes the macro to freeze up.
I nested another if statement to capture if there is only one line of data visible, but I cannot get it to function correctly. Any Suggestions on the nested if statement?
Dim criteria As String
Dim F As Range
Set Rng = Sheets("Reference").Range("W2:W36")
For Each F In Rng
criteria = F
ActiveSheet.Range("$AV$1").AutoFilter Field:=48, Criteria1:="=*BULK SUBSERVIENT*", Operator:=xlAnd
ActiveSheet.Range("$K$1").AutoFilter Field:=11, Criteria1:=criteria
Range("A2:CM" & ActiveSheet.UsedRange.Rows.Count + 1) _
.Cells.SpecialCells(xlCellTypeVisible).Rows(1).Select
If ActiveCell.Value = vbNullString Then
ActiveSheet.ShowAllData
Else
If (ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell)) = 2 Then
'Range(Selection).Select
Selection.Copy
Sheets("Bulk Subservient").Select
ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
Call cleanup
Else
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Bulk Subservient").Select
ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
Call cleanup
End If
End If
Next F
I figured it out.... Here is what I did. Thanks all!
I used this If ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible).Areas.Count <= 2
instead of this (ActiveSheet.UsedRange.SpecialCells(xlCellTypeLastCell)) = 2
Dim criteria As String
Dim F As Range
Set Rng = Sheets("Reference").Range("W2:W36")
For Each F In Rng
criteria = F
ActiveSheet.Range("$AV$1").AutoFilter Field:=48, Criteria1:="=*BULK SUBSERVIENT*", Operator:=xlAnd
ActiveSheet.Range("$K$1").AutoFilter Field:=11, Criteria1:=criteria
Range("A2:CM" & ActiveSheet.UsedRange.Rows.Count + 1) _
.Cells.SpecialCells(xlCellTypeVisible).Rows(1).Select
If ActiveCell.Value = vbNullString Then
ActiveSheet.ShowAllData
Else
If ActiveSheet.UsedRange.SpecialCells(xlCellTypeVisible).Areas.Count <= 2 Then
'Range(Selection).Select
Selection.Copy
Sheets("Bulk Subservient").Select
ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
Call cleanup
Else
Range(Selection, Selection.End(xlDown)).Select
Selection.Copy
Sheets("Bulk Subservient").Select
ActiveSheet.Range("A" & Rows.Count).End(xlUp).Offset(1).Select
ActiveSheet.Paste
Call cleanup
End If
End If
Next F
I think your code could be much cleaner than this. I prefer to use an auxiliar funcion to make this filter. Something like this:
Function MyFilter(criteria as string) as Range
Set tableRange = ActiveSheet.UsedRange
' Filter
With tableRange
Call .AutoFilter(48, "*BULK SUBSERVIENT*")
Call .AutoFilter(11, criteria)
End With
On Error Resume Next
'This...
Set selectedRange = tableRange.SpecialCells(xlCellTypeVisible)
'...Or (how to remover title).
Set selectedRange = Intersect(tableRange.SpecialCells(xlCellTypeVisible), .[2:1000000])
On Error GoTo 0
With tableRange
Call .AutoFilter(11)
Call .AutoFilter(48)
End With
'Empty Criteria
If WorksheetFunction.CountA(selectedRange) < 2 Then
Exit Sub
End If
Set MyFilter = selectedRange
End Sub
Here is your original code rewritten using the Range.CurrentRegion property to define the range of cells to be filtered.
Dim criteria As String
Dim F As Range, rng As Range
With Worksheets("Reference")
Set rng = .Range(.Cells(2, 23), .Cells(Rows.Count, 23).End(xlUp))
End With
With ActiveSheet '<~~ set this to worksheets("Sheet1") as appropriate
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
For Each F In rng
criteria = F
.AutoFilter Field:=48, Criteria1:="*BULK SUBSERVIENT*"
.AutoFilter Field:=11, Criteria1:=criteria
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
.Copy Destination:=Sheets("Bulk Subservient").Cells(Rows.Count, 1).End(xlUp).Offset(1)
End If
End With
Next F
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
Here is the same thing that collects all of the criteria terms from the Reference worksheet into a variant array and uses that to filter for all terms at once.
Dim rng As Range
Dim vCRITERIA As Variant, v As Long
With Worksheets("Reference")
ReDim vCRITERIA(1 To 1) '<~~for alternate method
For Each rng In .Range(.Cells(2, 23), .Cells(Rows.Count, 23).End(xlUp))
vCRITERIA(UBound(vCRITERIA)) = rng.Value2
ReDim Preserve vCRITERIA(UBound(vCRITERIA) + 1)
Next rng
ReDim Preserve vCRITERIA(UBound(vCRITERIA) - 1)
End With
With ActiveSheet '<~~ set this to worksheets("Sheet1") as appropriate
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
.AutoFilter Field:=48, Criteria1:="*BULK SUBSERVIENT*"
.AutoFilter Field:=11, Criteria1:=(vCRITERIA), Operator:=xlFilterValues
With .Resize(.Rows.Count - 1, .Columns.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
.Copy Destination:=Sheets("Bulk Subservient").Cells(Rows.Count, 1).End(xlUp).Offset(1)
End If
End With
End With
If .AutoFilterMode Then .AutoFilterMode = False
End With
The latter is likely a few milli-seconds faster than the first.
The worksheet's SUBTOTAL function never includes filtered or hidden rows so asking for a count will determine if there is anything to copy. Resizing and offsetting moves to the filtered range.
You will need to reincorporate the Cleanup subroutine.

Resources