arraysexcelvbaautofilter

How can i transfer Visible Filtered Data from a table into a multi dimensional array?


I have a table called "Sales Tracker" I did an auto filter and got my results. now i want to transfer the visible fields to my array called soArray the first time i go thru the cells it works fine and i get the 4 fields i need into the array, however, after the first time i get a "Run-Time error'9': Subscript out of range" what am i doing wrong? any help or push would be appreciated, thank you below is my code

    Sub getAllSO()

    Dim wkb As Workbook
    Dim wks As Worksheet
    
    Set wkb = Workbooks("ScrubCentral.xlsm")
    Set wks = wkb.Sheets("Sales Tracker")
    
    Dim tbl As ListObject
    Set tbl = wks.ListObjects("SalesTracker")
    tbl.AutoFilter.ShowAllData
    
    tbl.Range.AutoFilter field:=4, Criteria1:=">0"
    
    Dim soArray() As Variant
    Dim rngVisible As Range
    Dim i As Long
    Dim rCell As Range
    
    With wks
        With .AutoFilter.Range
            Set rngVisible = .Offset(1, 0).Resize(.Rows.Count - 1, 1) _
                  .SpecialCells(xlCellTypeVisible)
        End With
        
        For Each rCell In rngVisible
            i = i + 1
            ReDim Preserve soArray(1 To i, 1 To 4)
            soArray(i, 1) = rCell(i, 2)
            soArray(i, 2) = rCell(i, 3)
            soArray(i, 3) = rCell(i, 4)
            soArray(i, 4) = rCell(i, 13)
            Debug.Print soArray(i, 1), soArray(i, 2), soArray(i, 3), soArray(i, 4)
        Next rCell
    End With
    
End Sub

Solution

    1. If you use the Preserve keyword, you can resize only the last array dimension.

    Microsoft documentation:

    ReDim statement

    1. For Each rCell In rngVisible is equivalent to For Each rCell In rngVisible.Cells and does not extract data as your expected.

    2. Moreover, a filtered table (ListObject) often consists of non-contiguous ranges. To handle this properly, the script requires nested For Each loops.

    Sub getAllSO()
        Dim wkb As Workbook
        Dim wks As Worksheet
        Set wkb = Workbooks("ScrubCentral.xlsm")
        Set wks = wkb.Sheets("Sales Tracker")
        Dim tbl As ListObject
        ' Set wks = ActiveSheet ' for debugging
        Set tbl = wks.ListObjects("SalesTracker")
        tbl.AutoFilter.ShowAllData
        tbl.Range.AutoFilter field:=4, Criteria1:=">0"
        Dim soArray() As Variant
        Dim rngVisible As Range
        Dim i As Long
        With wks
            With .AutoFilter.Range
                Set rngVisible = tbl.DataBodyRange.SpecialCells(xlCellTypeVisible)
            End With
            Dim rArea As Range, rRow As Range
            For Each rArea In rngVisible.Areas
                For Each rRow In rArea.Rows
                    i = i + 1
                    ReDim Preserve soArray(1 To 4, 1 To i)
                    soArray(1, i) = rRow.Cells(2)
                    soArray(2, i) = rRow.Cells(3)
                    soArray(3, i) = rRow.Cells(4)
                    soArray(4, i) = rRow.Cells(13)
                Next
            Next
        End With
        ' for testing
        Sheets.Add
        Range("A1").Resize(i, 4).Value = Application.Transpose(soArray)
    End Sub
    

    If the filtering criteria in your script are as straightforward as shown, an alternative approach could be loading the table into an array and applying the filtering logic using VBA.

    Sub getAllSO2()
        Dim wkb As Workbook
        Dim wks As Worksheet
        Set wkb = Workbooks("ScrubCentral.xlsm")
        Set wks = wkb.Sheets("Sales Tracker")
        Dim tbl As ListObject
    '    Set wks = ActiveSheet ' for debugging
        Set tbl = wks.ListObjects("SalesTracker")
        tbl.AutoFilter.ShowAllData
        Dim loArray: loArray = tbl.DataBodyRange.Value
        Dim soArray() As Variant
        Dim i As Long, j As Long
        Const KEY_COL = 4
        For j = LBound(loArray) To UBound(loArray)
            If loArray(j, KEY_COL) > 0 Then ' filtering table
                i = i + 1
                ReDim Preserve soArray(1 To 4, 1 To i)
                soArray(1, i) = loArray(j, 2)
                soArray(2, i) = loArray(j, 3)
                soArray(3, i) = loArray(j, 4)
                soArray(4, i) = loArray(j, 13)
            End If
        Next
        ' for testing
        Sheets.Add
        Range("A1").Resize(i, 4).Value = Application.Transpose(soArray)
    End Sub
    

    An alternative to repeated ReDim statements is to pre-allocate sufficient array space to avoid frequent resizing.

    Sub getAllSO3()
        Dim wkb As Workbook
        Dim wks As Worksheet
        Set wkb = Workbooks("ScrubCentral.xlsm")
        Set wks = wkb.Sheets("Sales Tracker")
        Dim tbl As ListObject
    '    Set wks = ActiveSheet ' for debugging
        Set tbl = wks.ListObjects("SalesTracker")
        tbl.AutoFilter.ShowAllData
        Dim loArray: loArray = tbl.DataBodyRange.Value
        Dim soArray() As Variant
        Redim soArray(1 To UBound(loArray), 1 to 4)
        Dim i As Long, j As Long
        Const KEY_COL = 4
        For j = LBound(loArray) To UBound(loArray)
            If loArray(j, KEY_COL) > 0 Then ' filtering table
                i = i + 1
                ReDim Preserve soArray(1 To 4, 1 To i)
                soArray(i, 1) = loArray(j, 2)
                soArray(i, 2) = loArray(j, 3)
                soArray(i, 3) = loArray(j, 4)
                soArray(i, 4) = loArray(j, 13)
            End If
        Next
        ' for testing
        Sheets.Add
        Range("A1").Resize(i, 4).Value = soArray
    End Sub