Select only first 5 values of a column after applying a filter to a particular column on a particular condition,without duplicates - excel

i have applied autofilter to the column,that part pf the code is running properly ,but on that condition there are suppose 20 values in that column but i want only 5 ,any particular code would help
Dim rFirstFilteredRow As Range
With Worksheets("Sheet1")
With .Cells(1, 1).CurrentRegion
ActiveSheet.Range("$A$1:$D$6").AutoFilter Field:=4, Criteria1:="1"
With .Resize(.Rows.Count - 1, .Rows.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
Set rFirstFilteredRow = _
.SpecialCells(xlCellTypeVisible).Columns(2).Cells
rFirstFilteredRow.Copy
Range("G16").Select
ActiveSheet.Paste
End If
End With
End With
End With
End Sub
this helps in getting first column after filter but not the first five

Just add .Resize(5) when setting the width of rFirstFilteredRow to resize the selection to 5 rows high.
Example below (I shortened the code a lot):
Sub Answer()
Dim rFirstFilteredRow As Range
ActiveSheet.Range("$A$1:$D$6").AutoFilter Field:=4, Criteria1:="1"
With Worksheets("Sheet1").Cells(1, 1).CurrentRegion
With .Resize(.Rows.Count - 1, .Rows.Count).Offset(1, 0)
If CBool(Application.Subtotal(103, .Cells)) Then
' Select first 5 columns starting at column 2
Set rFirstFilteredRow = _
.SpecialCells(xlCellTypeVisible).Columns(2).Resize(5)
rFirstFilteredRow.Copy
Range("G16").Select
ActiveSheet.Paste
End If
End With
End With
End Sub

Sub macro2()
Const MAXROWS = 5
Dim ws As Worksheet, rng As Range
Dim i As Long, c As Range
Set ws = ThisWorkbook.Worksheets("Sheet1")
ws.Range("$A$1:$D$6").AutoFilter Field:=4, Criteria1:="1"
With ws.Cells(1, 1).CurrentRegion.Columns(2)
Set rng = .Cells.SpecialCells(xlCellTypeVisible)
End With
i = 0
For Each c In rng.Cells
If i > 0 Then ' skip header
ws.Range("G16").Offset(i - 1) = c.Value2
End If
i = i + 1
If i > MAXROWS Then Exit For
Next
End Sub

Related

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

Deleting Multiple Rows based on a Set Criteria

Sub Macro()
Dim i As Long
For i = Cells(Rows.Count, 14).End(xlUp).Row To 2 Step -1
If Cells(i, 14).Value2 = "APPLE" Then
Rows(i).Delete
End If
Next i
Dim f As Long
For f = Cells(Rows.Count, 14).End(xlUp).Row To 2 Step -1
If Cells(f, 14).Value2 = "NAME" Then
Rows(f).Delete
End If
Next f
End Sub
I have the above mentioned code to delete all the rows that have apple and name on them, If possible I would like excel to execute the code in one or two lines. Your help would be greatly appreciated!
Dim i As Long
For i = Cells(Rows.Count, 14).End(xlUp).Row To 2 Step -1
IF Cells(i, 14).Value2 = "APPLE" OR Cells(i, 14).Value2 = "NAME" THEN Rows(i).Delete
Next i
The fast way to delete rows is using AutoFilter:
Sub FastDelete()
Dim rng As Range, rngVisible As Range
'//Remove filter if any
ActiveSheet.AutoFilterMode = False
'// Get range of only one column (N)
Set rng = Range(Cells(1, 14), Cells(Rows.Count, 14).End(xlUp))
'// Field:=1 because filter has only one field
rng.AutoFilter Field:=1, Criteria1:=Array("APPLE", "NAME"), Operator:=xlFilterValues
'// Have error handling in case if no data is found
On Error Resume Next
With rng
'// Use Offset and Resize to exclude header
Set rngVisible = .Offset(1).Resize(.Rows.Count - 1).SpecialCells(xlCellTypeVisible)
End With
'// If rows were found (i.e. there's no error), delete them
If Err = 0 Then rngVisible.EntireRow.Delete
On Error GoTo 0
'// Remove filter
ActiveSheet.AutoFilterMode = False
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

Find data and move to prior cell and find again using active cell value - problems faced

Its update to my prior question for which i missed to add point saying that column 3 Header data might start with space or at the end or any additional text in it hence we should try it with contains.
Count results should be shown in a new sheet for all filter entities like 3 (Index) 3(Level) AIUH (Entity Name) 3(Count) with additional column to the end of the table and rows will not be
I apologize for my bad etiquette and wasting experts time on this to work again.
Here is the previous code for reference:
Sub xferAscendingFiltered()
Dim cnt As Long, rHDR As Range, rDELs As Range, vFLTRs As Variant
'fill this array with your 40-50 Header values
vFLTRs = Array("AIS", "BBS", "AIUH", _
"XXX", "YYY", "ZZZ")
With Worksheets("Sheet2")
If .AutoFilterMode Then .AutoFilterMode = False
With .Cells(1, 1).CurrentRegion
'filter on all the values in the array
.AutoFilter Field:=3, Criteria1:=vFLTRs, Operator:=xlFilterValues
'walk through the visible rows
With .Resize(.Rows.Count - 1, 1).Offset(0, 2)
Set rHDR = .Find(What:=Chr(42), After:=.Cells(1, 1), _
SearchOrder:=xlByRows, SearchDirection:=xlNext)
'seed the rows to delete so Union can be used later
If rHDR.Row > 1 Then _
Set rDELs = rHDR
Do While rHDR.Row > 1
cnt = 0
'increase cnt by both visible and hidden cells
Do
cnt = cnt + 1
Loop While rHDR.Offset(cnt, -1).Value2 > rHDR.Offset(cnt - 1, -1).Value2 And _
Intersect(rHDR.Offset(cnt, 0), .SpecialCells(xlCellTypeVisible)) Is Nothing
'transfer the values and clear the original(s)
With .Cells(rHDR.Row, 1).Resize(cnt, 3).Offset(0, -2)
'transfer the values
Worksheets("Sheet3").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Resize(.Rows.Count, .Columns.Count) = .Value
'set teh count
Worksheets("Sheet3").Cells(Rows.Count, 1).End(xlUp).Offset(1 - cnt, 3) = cnt
Set rDELs = Union(rDELs, .Cells)
rHDR.Clear
End With
'get next visible Header in column C
Set rHDR = .FindNext(After:=.Cells(1, 1))
Loop
.AutoFilter
End With
End With
'remove the rows
rDELs.EntireRow.Delete
End With
End Sub
Prior question link:
Thanks experts
Wildcards in your filter code.
To use contains using a variable, this should work as the criteria to find:
This will loop through the array and place a 1 beside a match, then filter column D for 1
Sub xferAscendingFiltered()
Dim cnt As Long, rHDR As Range, rDELs As Range, vFLTRs As Variant
'-------------
Dim rng As Range, cel As Range, LstRw As Long, sh As Worksheet, i '<<<<<
Set sh = Sheets("Sheet2") '<<<<<<<<
'---------------
'fill this array with your 40-50 Header values
vFLTRs = Array("AIUH", "ASC", "ABB", "BBS", "YYY", "ZZZ")
'vFLTRs = Array("*BBS*", "*ABB*", "*ASC*", "*AIUH*")
With sh
'-----------------------------------<<<<<<
LstRw = .Cells(.Rows.Count, "C").End(xlUp).Row
Set rng = Range("C2:C" & LstRw)
'----Loop Through Array-----
For i = LBound(vFLTRs) To UBound(vFLTRs)
For Each cel In rng.Cells
If cel Like "*" & vFLTRs(i) & "*" Then
cel.Offset(, 1) = 1
End If
Next cel
Next i
With .Cells(1, 1).CurrentRegion
'filter on all the values in the array
.AutoFilter Field:=4, Criteria1:=1
'-----------------------------------<<<<<<<<<
'walk through the visible rows
With .Resize(.Rows.Count - 1, 1).Offset(0, 2)
Set rHDR = .Find(What:=Chr(42), After:=.Cells(1, 1), SearchOrder:=xlByRows, SearchDirection:=xlNext)
'seed the rows to delete so Union can be used later
If rHDR.Row > 1 Then Set rDELs = rHDR
Do While rHDR.Row > 1
cnt = 0
'increase cnt by both visible and hidden cells
Do
cnt = cnt + 1
Loop While rHDR.Offset(cnt, -1).Value2 > rHDR.Offset(cnt - 1, -1).Value2 And _
Intersect(rHDR.Offset(cnt, 0), .SpecialCells(xlCellTypeVisible)) Is Nothing
'transfer the values and clear the original(s)
With .Cells(rHDR.Row, 1).Resize(cnt, 3).Offset(0, -2)
Worksheets("Sheet3").Cells(Rows.Count, 1).End(xlUp).Offset(1, 0).Resize(.Rows.Count, .Columns.Count) = .Value
Set rDELs = Union(rDELs, .Cells)
rHDR.Clear
End With
'get next visible Header in column C
Set rHDR = .FindNext(After:=.Cells(1, 1))
Loop
.AutoFilter
End With
End With
'remove the rows
rDELs.EntireRow.DELETE
End With
End Sub

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