6

I use this formula to copy unique records from Column A into Column B.

Range("A1", Range("A100").End(xlUp)).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=Range("B1"), Unique:=True

Instead of copying it into Column B how do you put the filtered results into an array in Excel VBA?

MrPatterns
  • 4,184
  • 27
  • 65
  • 85
  • 2
    There's no built-in method for that. You could copy to a temporary location (such as a hidden sheet) and from there transfer to an array. – Tim Williams Aug 16 '12 at 21:45
  • Ah ok. Is there a way accomplish the same result using only code and not cells on a hidden worksheet? That is, the result being find all unique records in Range A1:A100 (avoid duplicate values) and put them into an array. – MrPatterns Aug 16 '12 at 21:50

7 Answers7

6

It has been exactly a year since this question was asked but I ran into the same problem today and here is my solution for it:

Function copyFilteredData() As Variant
    Dim selectedData() As Variant
    Dim aCnt As Long
    Dim rCnt As Long

    Range("A1").CurrentRegion.SpecialCells(xlCellTypeVisible).Select
    On Error GoTo MakeArray:
    For aCnt = 1 To Selection.Areas.Count
        For rCnt = 1 To Selection.Areas(aCnt).Rows.Count
            ReDim Preserve SelectedData(UBound(selectedData) + 1)
            selectedData(UBound(selectedData)) = Selection.Areas(aCnt).Rows(rCnt)
        Next
    Next

    copyFilteredData = selectedData
    Exit Function

MakeArray:
    ReDim selectedData(1)
    Resume Next

End Function 

This will leave element 0 of the array empty but UBound(SelectedData) returns the number of rows in the selection

Johan G
  • 407
  • 5
  • 12
  • 3
    Redim is a very costly action. I wouldn't use it in a loop like that, unless the number of expected iterations is very low. – Mor Sagmon Aug 11 '16 at 18:41
  • @MorSagmon Can you explain in which way the redim is costly? – Johan G Aug 12 '16 at 05:49
  • 2
    You should also [avoid using select](https://stackoverflow.com/questions/10714251/how-to-avoid-using-select-in-excel-vba-macros) and declare some range to operate on in place of `Selection`. Other than that, nice methodology – Wolfie Aug 30 '17 at 08:28
4

Just in case anyone ever looks at this again... I created this function to work on a 1-D range but it will also write a higher dimension range to a 1-D array; it shouldn't be too hard to modify to write a multiple dimension range to a "same shape" array. You need to have a reference to scrrun.dll to create the dictionary object. Scaling may be a problem since a "for each" loop is used but if you are using EXCEL this is likely nothing you are worried about:

Function RangeToArrUnique(rng As Range)
    Dim d As Object, cl As Range
    Set d = CreateObject("Scripting.Dictionary")
    For Each cl In rng
        d(cl.Value) = 1
    Next cl
    RangeToArrUnique = d.keys
End Function

I've tested this in this way:

Dim dat as worksheet
set dat = sheets("Data")
roomArr = Array("OR01","OR02","OR03")
dat.UsedRange.AutoFilter field:=2, criteria1:=roomArr, operator:=xlFilterValues
fltArr = RangeToArrUnique(dat.UsedRange.SpecialCells(CellTypeVisible))

Hope this helps someone out there!

mmurrietta
  • 191
  • 3
2
Sub tester()

    Dim arr
    arr = UniquesFromRange(ActiveSheet.Range("A1:A5"))
    If UBound(arr) = -1 Then
        Debug.Print "no values found"
    Else
        Debug.Print "got array of unique values"
    End If

End Sub


Function UniquesFromRange(rng As Range)
    Dim d As Object, c As Range, tmp
    Set d = CreateObject("scripting.dictionary")
    For Each c In rng.Cells
       tmp = Trim(c.Value)
       If Len(tmp) > 0 Then
            If Not d.Exists(tmp) Then d.Add tmp, 1
       End If
    Next c
    UniquesFromRange = d.keys
End Function
Tim Williams
  • 154,628
  • 8
  • 97
  • 125
1

You will want to Read this and it will point you in the right direction

It says:

  1. Use the AdvancedFilter method to create the filtered range in some unused area of a worksheet
  2. Assign the Value property of that range to a Variant to create a two-dimensional array
  3. Use the ClearContents method of that range to get rid of it
Dick Kusleika
  • 32,673
  • 4
  • 52
  • 73
Sorceri
  • 7,870
  • 1
  • 29
  • 38
  • Please post the steps, or at least a screenshot of this link rather than just a naked link. – brettdj Aug 16 '12 at 23:29
  • Sorceri, the code at the very bottom of this link is the EXACT answer I needed. It replicates the filter into an array – MrPatterns Aug 17 '12 at 01:21
  • Performance wise wouldn't it be better to put the entire set of data into an array then build the array you want from that rather than moving it out into an excel spreadsheet, reading it back into an array and then clearing that data from the spreadsheet? – MorkPork Jan 12 '15 at 21:03
1

A simple way to store a filtered range in an array is to use the copy-paste trick. Create a worksheet and make it hidden or very hidden. Say its code name is sht_calc. This function will give you a 2D array unless you only have one column and the filtered rows are only one, which in that case it will be a simple variant variable and not an array

Function GetArrayFromFilteredRange(rng As Range) As Variant
    Dim arr As Variant
    
    sht_calc.Cells.Clear
    rng.Copy sht_calc.Range("A1")
    arr = sht_calc.UsedRange.Value
    
    GetArrayFromFilteredRange = arr
End Function

For example if you want to get the array of filtered rows in a table called Table1 in a worksheet with a code name of sht1 you can simply do this:

dim rng as range
arr = GetArrayFromFilteredRange(sht1.ListObjects("Table1").DataBodyRange.SpecialCells(xlCellTypeVisible))

arr=GetArrayFromFilteredRange(rng)
Ibo
  • 4,081
  • 6
  • 45
  • 65
1

Here's another way to do it. If there are no results it just does nothing.

Public Sub filteredRangeToArray(rg As Range, arr As Variant)

Dim i As Long
Dim j As Long
Dim row As Range
'If 0 results in Filter just exit
If Not rg.SpecialCells(xlCellTypeVisible).Count > 0 Then Exit Sub
i = 1
Erase arr
ReDim arr(1 To rg.Columns.Count, 1 To _
   rg.Columns(1).SpecialCells(xlCellTypeVisible).Count)
For Each row In rg.Rows
 If Not row.Hidden Then
  For j = LBound(arr, 1) To UBound(arr, 1)
  arr(j, i) = row.Cells(j)
  Next j
  i = i + 1
 End If
Next row
arr = WorksheetFunction.Transpose(arr)
End Sub
Charlio
  • 346
  • 4
  • 14
0

The following takes information from column A and gives a list. It assumes you have a "Sheet3" which is available for data input (you may wish to change this).

Sub test()

    Dim targetRng As Range
    Dim i As Integer

    Set targetRng = Sheets(3).Range("a1")
    Range("A1", Range("A999").End(xlUp)).AdvancedFilter Action:=xlFilterCopy, CopyToRange:=targetRng, Unique:=True

    Dim numbElements As Integer
    numbElements = targetRng.End(xlDown).Row
    Dim arr() As String

    ReDim arr(1 To numbElements) As String

    For i = 1 To numbElements
        arr(i) = targetRng.Offset(i - 1, 0).Value
    Next i

End Sub
enderland
  • 13,825
  • 17
  • 98
  • 152