arraysexcelvbadictionaryautofilter

Autofilter the already Autofilterd column


I am using a custom Autofilter by use of a dictionary as in the below code.
Now, I need to set an additional autofilter on the already filtered column.
e.g. the result of the first autofilter is ("ID 20 , Name30 , Color 35 , ID39"),
on the second autofilter, I need to filter for a string contains e.g. "*30*".
I need to do it in a second separate step, I mean, after placing the first filter in the way I tried and then I closed that workbook completely (to be eloquent), I need then to apply the second filter on the already filtered range, by cells containing 30. or meaning apply filter on the data found on the visible cells only.
Note, I do not prefer to use a a helper column/sheet ,
and also want to keep my code as it is, meaning I seek for additional sub.
In advance, great thanks for your time of help.

Option Explicit
Option Compare Text

Sub Filter_the_Filtered_Column()

    Const filter_Column As Long = 2
    
    Dim filter_Criteria() As Variant
    filter_Criteria = Array("*Id*", "*Name*", "*Color*")
    
    Dim ws As Worksheet:    Set ws = ActiveSheet
    
    If ws.AutoFilterMode Then ws.AutoFilterMode = False
    
    Dim rg As Range
    Set rg = ws.UsedRange.Resize(ws.UsedRange.Rows.count - 1).Offset(1)  '(UsedRange except the first Row)
    
    Dim rCount As Long, arr() As Variant, dict As Object, el, r As Long
    rCount = rg.Rows.count - 1
    arr = rg.Columns(filter_Column).Resize(rCount).Offset(1).value         'Write the values from criteria column to an array.
        
    Set dict = CreateObject("Scripting.Dictionary")                        'Write the matching strings to the keys (a 1D array) of a dictionary.
 
    For r = 1 To UBound(arr)                                                'Loop through the elements of the array.
        For Each el In filter_Criteria
            If arr(r, 1) Like el Then dict(arr(r, 1)) = vbNullString: Exit For
        Next el
    Next r
    
    If dict.count > 0 Then
        rg.AutoFilter Field:=filter_Column, Criteria1:=dict.Keys, Operator:=xlFilterValues   'use the keys of the dictionary (a 1D array) as a Criteria
    End If

End Sub

Solution

  • If you do want it as a separated sub, you can try this:

    Sub filteredRangeToArray()
        Const filter_Column As Long = 2
        
        Dim ws As Worksheet, arr(), rng As Range, newSh As Worksheet
        Dim lRow As Long, r As Long
        Dim nFilter As String: nFilter = "30"
        Dim dict As Object
        
        Set ws = ActiveWorkbook.ActiveSheet
        lRow = ws.Range("A" & Rows.Count).End(xlUp).Row
        Set rng = ws.Range("A2:A" & lRow).SpecialCells(xlCellTypeVisible)
        Application.ScreenUpdating = False
        Application.DisplayAlerts = False
        Set newSh = Worksheets.Add
        rng.Copy newSh.Range("A1")
        arr = newSh.Range("A1:A" & rng.Count).Value
        newSh.Delete
        Application.ScreenUpdating = True
        Application.DisplayAlerts = True
        
        Set dict = CreateObject("Scripting.Dictionary")
        For r = 1 To UBound(arr, 1)
            If arr(r, 1) Like nFilter Then dict(arr(r, 1)) = vbNullString
        Next r
        
        If dict.Count > 0 Then
            rg.AutoFilter Field:=filter_Column, Criteria1:=dict.Keys, Operator:=xlFilterValues   'use the keys of the dictionary (a 1D array) as a Criteria
        End If
    End Sub 
    

    But now that I see FaneDuru's comment about making the dict a global variable, it could be a lot faster/easier of course. I'll leave my answer for when you don't want to use that.

    Edit:

    Sub filteredRangeToArray_V2()
        Const filter_Column As Long = 2
        
        Dim ws As Worksheet, arr(), rng As Range, newSh As Worksheet
        Dim lRow As Long, r As Long
        Dim nFilter As String: nFilter = "*30*"
        Dim dict As Object
        Dim ccell As Range
        
        Set ws = ActiveWorkbook.ActiveSheet
        lRow = ws.Range("A" & Rows.Count).End(xlUp).Row
        Set rng = ws.Range("A2:A" & lRow).Offset(0, filter_Column - 1).SpecialCells(xlCellTypeVisible)
        
        ReDim arr(1 To rng.Count)
        r = 1
        For Each ccell In rng.Cells
            arr(r) = ccell.Value
            r = r + 1
        Next ccell
        Set dict = CreateObject("Scripting.Dictionary")
        For r = 1 To UBound(arr, 1)
            If arr(r) Like nFilter Then dict(arr(r)) = vbNullString
        Next r
        
        Set rng = ws.UsedRange.Resize(ws.UsedRange.Rows.Count)
        rng.Select
        If ws.AutoFilterMode Then ws.AutoFilterMode = False
        If dict.Count > 0 Then
            rng.AutoFilter Field:=filter_Column, Criteria1:=dict.Keys, Operator:=xlFilterValues   'use the keys of the dictionary (a 1D array) as a Criteria
        End If
    End Sub
    

    This should accommodate the not needing another sheet/helper-column.