+ Reply to Thread
Results 1 to 15 of 15

Create an array for filtered and non-contiguous columns

Hybrid View

  1. #1
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Create an array for filtered and non-contiguous columns

    Hello everyone
    I have in my file filtered data based on column G

    I need to create an array of the values of these filtered data for two columns (B & G) only

    In other word to store the values of these two filtered columns (visible rows only) not all the values
    Attached Files Attached Files
    < ----- Please click the little star * next to add reputation if my post helps you
    Visit Forum : From Here

  2. #2
    Forum Expert
    Join Date
    01-23-2013
    Location
    USA
    MS-Off Ver
    Microsoft 365 aka Office 365
    Posts
    3,863

    Re: Create an array for filtered and non-contiguous columns

    Hi,

    I gave you what you asked for, but also added what you probably need instead:
    a. You asked for a two dimensional array
    b. I think you need a one dimensional data structure which contains the two columns.

    Notice that the array is rather cryptic, because you have to rely on the index numbers to determine whether you have an Item or a Mount (Amount). With the data structure you know right away. Of course, you could declare constants for each of the array indices that indicate 'Item' or 'Mount.

    The difference is (simplified non-working examples)
    'Array:
    Dim vArray(1 to 2, 1 to 15) as Variant
    vArray(1,1) = contents of row 4 column 'B'
    vArray(2,1) = contents of row 4 column 'G'
    
    'Data Structure
    Public Type myStructure   'This must be at the top of the module
      sItemName As String
      iMount As Long              'your spreadsheet says Mount, you probably mean Amount
    End Type
    
    dim myStructureArray(1 to 15) as myStructure   
    myStructureArray(1).sItemName = contents of row 4 column 'B'
    myStructureArray(1).iMount = contents of row 4 column 'G'
    Actual working code in an ordinary code module such as Module1:
    Option Explicit
    
    Public Type myStructure
      sItemName As String
      iMount As Long
    End Type
    
    
    Sub TestCreateStructureArrayOfFilteredData()
    
      Dim ws As Worksheet
      Dim myStructureArray() As myStructure
    
      Dim i As Long
      
      'Create the Worksheet Object
      Set ws = Sheets("Store")
      
      'Create an Array of Filtered Data
      Call CreateStructureArrayOfFilteredData(ws, myStructureArray)
    
      'Output the array contents in the Immediate Window (CTRL G in the debugger)
      Debug.Print "myStructureArray output created on " & Now()
      For i = LBound(myStructureArray) To UBound(myStructureArray)
        Debug.Print i, myStructureArray(i).sItemName, myStructureArray(i).iMount
      Next i
      
      'Clear object pointers
      Set ws = Nothing
    
    End Sub
    
    Sub CreateStructureArrayOfFilteredData(ws As Worksheet, myStructureArray() As myStructure)
      'This puts the contents of Columns 'B' and 'G' in a Structure Array
      '
      'The first data row is row 4
      
      Const nFirstDataROW = 4
      
      Dim vB As Variant
      Dim vG As Variant
      Dim iLastIndex As Long
      Dim iRow As Long
      Dim iLastDataRow As Long
      
      'Get the Last Data Row
      iLastDataRow = ws.Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
      
      ReDim myStructureArray(1 To 1)
      iLastIndex = 0
      
      For iRow = nFirstDataROW To iLastDataRow
      
        If ws.Rows(iRow).EntireRow.Hidden = False Then
          vB = ws.Cells(iRow, "B").Value
          vG = ws.Cells(iRow, "G").Value
          
          iLastIndex = iLastIndex + 1
          ReDim Preserve myStructureArray(1 To iLastIndex)
          myStructureArray(iLastIndex).sItemName = CStr(vB)
          myStructureArray(iLastIndex).iMount = CLng(vG)
        End If
      
      Next iRow
      
    
    End Sub
    
    Sub TestCreateArrayOfFilteredData()
      'Test Routine to Create a 2 dimensional Array of Variant data
    
      Dim ws As Worksheet
      Dim vArray As Variant
    
      Dim i As Long
      
      'Create the Worksheet Object
      Set ws = Sheets("Store")
      
      'Create an Array of Filtered Data
      Call CreateArrayOfFilteredData(ws, vArray)
    
      'Output the array contents in the Immediate Window (CTRL G in the debugger)
      Debug.Print "vArray output created on " & Now()
      For i = LBound(vArray, 2) To UBound(vArray, 2)
        Debug.Print i, vArray(1, i), vArray(2, i)
      Next i
      
      'Clear object pointers
      Set ws = Nothing
    
    End Sub
    
    Sub CreateArrayOfFilteredData(ws As Worksheet, vArray As Variant)
      'Routine to Create a 2 dimensional Array of Variant data
      '
      'This puts the contents of Column 'B' visible rows only in vArray(1,n)
      'This puts the contents of Column 'G' visible rows only in vArray(2,n)
      '
      'The first data row is row 4
      
      Const nFirstDataROW = 4
      
      Dim vB As Variant
      Dim vG As Variant
      Dim iLastIndex As Long
      Dim iRow As Long
      Dim iLastDataRow As Long
      
      'Get the Last Data Row
      iLastDataRow = ws.Cells.Find(What:="*", SearchOrder:=xlByRows, SearchDirection:=xlPrevious).Row
      
      ReDim vArray(1 To 2, 1 To 1)
      iLastIndex = 0
      
      For iRow = nFirstDataROW To iLastDataRow
      
        If ws.Rows(iRow).EntireRow.Hidden = False Then
          vB = ws.Cells(iRow, "B").Value
          vG = ws.Cells(iRow, "G").Value
          
          iLastIndex = iLastIndex + 1
          ReDim Preserve vArray(1 To 2, 1 To iLastIndex)
          vArray(1, iLastIndex) = vB
          vArray(2, iLastIndex) = vG
        End If
      
      Next iRow
      
    End Sub
    Lewis
    Attached Files Attached Files
    Last edited by LJMetzger; 07-28-2015 at 08:17 AM. Reason: Replaced download file due to typos that caused compile errors. Posted code was correct. Sorry. Lewis

  3. #3
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    Thanks a lot mr. Lewis for this perfect way ..
    It is excellent but I didn't imagine it would be so long way.
    The two ways are wonderful ..

    Is there an easy way to create UDF function that deal with arrays and we as beginner users use the UDF to have the results quickly?
    I imagine this : CreateFilteredArray(WholeRangeOfData,Column B,Column G)
    =CreateFilteredArray(Range("A4:G92"),2,7)

    Thanks for your great and awesome help

  4. #4
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: Create an array for filtered and non-contiguous columns

    Try this out:

    CopyFiltered.xlsm

    Public Sub Copy()
        'copy columns 2 and 6 of filtered data to Sheet2!A1
        Dim v As Variant
        v = GetFilteredArray(Worksheets("Sheet1"), 2, 6)
        'you now have the array. if you want to copy it to a sheet then ...
        Dim Target As Excel.Range
        Set Target = Worksheets("Sheet2").Range("A1")
        Target.Worksheet.Cells.ClearContents
        Set Target = Target.Resize(UBound(v, 1) + 1, UBound(v, 2) + 1)
        Target.Value = v
    End Sub
    
    Public Function GetFilteredArray(FilterSheet As Excel.Worksheet, ParamArray ColIndex()) As Variant
        'ColIndex - list of column indexes you want returned, 1=first col
        Dim DataObj As New MSForms.DataObject
        Dim Rows As Variant, Cols As Variant
        Dim r  As Long, c As Long, v As Variant
        FilterSheet.AutoFilter.Range.Copy
        DataObj.GetFromClipboard
        Rows = Split(DataObj.GetText, vbCrLf)
        DataObj.Clear
        ReDim v(0 To UBound(Rows), 0 To UBound(ColIndex))
        For r = 0 To UBound(Rows)
            If Rows(r) = "" Then Exit For
            Cols = Split(Rows(r), vbTab)
            For c = 0 To UBound(ColIndex)
                v(r, c) = Cols(ColIndex(c) - 1)
            Next c
        Next r
        GetFilteredArray = v
    End Function

  5. #5
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    @ cyiangou
    Amazing UDF fuction. I consider it very useful too that can save time and effort
    Thanks a lot for this great and wonderful gift

    Just a little modification if possible ... the first parameter to be more spefici that's :
    Worksheets("Sheet1").range("A3:G3")

    v = GetFilteredArray(Worksheets("Sheet1").Range("A3:G3"), 2, 7)
    Range("A3:G3") is the title header of the filtered range
    Thanks alot for this great help
    Last edited by YasserKhalil; 07-26-2015 at 02:03 PM.

  6. #6
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    I noticed that there is a reference "Microsoft Forms 2.0 Object Library" and I tried to check it to activate it in my file but I didn't find the reference.
    I'm using office 2013

  7. #7
    Valued Forum Contributor
    Join Date
    09-17-2012
    Location
    Johannesburg, South Africa
    MS-Off Ver
    Excel 2007
    Posts
    454

    Re: Create an array for filtered and non-contiguous columns

    The UDF is using a helpful feature of the autofilter itself (.AutoFilter.Range.Copy) that lets us exclude the hidden rows, so it has to be a reference to an actual autofilter definition, else we can't use this. But you can only have one autofilter per sheet (I think), so isn't this where you'll want to point to anyway?

    If the code works without the reference, then you don't need it. Probably built in to 2013.

  8. #8
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    It works well with your attachment
    After copying the code to my file ,I got error "user-defined type not defined" Compile error
    Any idea about that

  9. #9
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    Thanks .. I could fix it ..
    I just need a little modification >> Just to add a line that checks auto filter mode .. if it is true then the code to be executed but if it is false to exit sub

  10. #10
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    Thanks everyone
    I have added this line to solve the problem
    If Worksheets("Store").FilterMode = False Then Exit Sub
    Thanks a lot for any help me even if with a simple idea

  11. #11
    Forum Expert
    Join Date
    04-23-2009
    Location
    Matrouh, Egypt
    MS-Off Ver
    Excel 2013
    Posts
    6,892

    Re: Create an array for filtered and non-contiguous columns

    I clicked Browse to add the reference and browse in system32 folder for FM20.DLL and add it
    I get rid of the error "User-Defined type not defined"

    After that I tested the code again and I got the following error
    FilterSheet.AutoFilter.Range.copy
    Object variable or with block variable not set

    Any idea

  12. #12
    Forum Expert
    Join Date
    01-23-2013
    Location
    USA
    MS-Off Ver
    Microsoft 365 aka Office 365
    Posts
    3,863

    Re: Create an array for filtered and non-contiguous columns

    Thanks for the rep points YasserKhalil.

    YasserKhalil wrote:
    Is there an easy way to create UDF function that deal with arrays and we as beginner users use the UDF to have the results quickly? I imagine this : CreateFilteredArray(WholeRangeOfData,Column B,Column G)
    =CreateFilteredArray(Range("A4:G92"),2,7)

    The function you want can not be used in a formula (see the next post for reasons):
    =CreateFilteredArray(Range("A4:G92"),2,7)   'This is NOT ALLOWED

    Try the following function (a slight modification of function CreateStructureArrayOfFilteredData() in post #2 above, which can be used in other VBA code. All the code that follows can be tested using the data from the file in post #2 above.
    Function CreateFilteredArray(myRange As Range, iColumn1 As Long, iColumn2 As Long) As Variant
      'This returns a variant two dimensional array containing data from the input range, but
      'only using data from the input columns (visible rows only)
    
      Dim r1 As Range
      Dim r2 As Range
      Dim vArray As Variant
      Dim v1 As Variant
      Dim v2 As Variant
      Dim iLastIndex As Long
      Dim iRow As Long
      Dim iFirstDataRow As Long
      Dim iLastDataRow As Long
      
      'Get the First and Last Data Rows
      iFirstDataRow = myRange.Row
      iLastDataRow = iFirstDataRow + myRange.Rows.Count - 1
      
      'Verify that the input columns are in the Input Range
      Set r1 = Intersect(myRange, Columns(iColumn1))
      Set r2 = Intersect(myRange, Columns(iColumn2))
      
      'Exit if 'iColumn1' is NOT in 'myRange'
      If r1 Is Nothing Then
        GoTo MYEXIT
      End If
      
      'Exit if 'iColumn2' is NOT in 'myRange'
      If r2 Is Nothing Then
        GoTo MYEXIT
      End If
      
      
      'Initialize the Variant array
      ReDim vArray(1 To 2, 1 To 1)
      iLastIndex = 0
      
      'Create the Variant Array
      For iRow = iFirstDataRow To iLastDataRow
      
        'Get the address where the current row and the current column intersects
        Set r1 = Intersect(myRange, Rows(iRow), Columns(iColumn1))
        Set r2 = Intersect(myRange, Rows(iRow), Columns(iColumn2))
      
        'Process the row if it is visible
        If r1.EntireRow.Hidden = False Then
        
          'Get the value for each column in the current row
          v1 = r1.Value
          v2 = r2.Value
          
          'Add the value to the Variant array
          iLastIndex = iLastIndex + 1
          ReDim Preserve vArray(1 To 2, 1 To iLastIndex)
          vArray(1, iLastIndex) = v1
          vArray(2, iLastIndex) = v2
          
        End If
      
      Next iRow
      
      'Clear object pointers
      Set r1 = Nothing
      Set r2 = Nothing
      
    MYEXIT:
    
      'Create the return array to be used by the calling routine
      CreateFilteredArray = vArray
    
    End Function
    The following are a few ways to test the above function.
    Sub TestCreateFilteredArray()
      'Test Routine to Create a 2 dimensional Array of Variant data
    
      Dim myRange As Range
      Dim vArray As Variant
    
      Dim i As Long
      
      ''''''''''''''''''''''''''''''''''''''''''''
      'Bad Input Column Number Example
      ''''''''''''''''''''''''''''''''''''''''''''
      vArray = CreateFilteredArray(Sheets("Store").Range("A4:G90"), 2, 22)
      'Output the array contents in the Immediate Window (CTRL G in the debugger)
      Debug.Print "vArray output created on " & Now()
      If IsEmpty(vArray) = True Then
        Debug.Print "CreateFilteredArray() had bad input data - vArray is EMPTY."
      Else
        For i = LBound(vArray, 2) To UBound(vArray, 2)
          Debug.Print i, vArray(1, i), vArray(2, i)
        Next i
      End If
      Debug.Print
    
    
      ''''''''''''''''''''''''''''''''''''''''''''
      'Create an Array of Valid Filtered Data Example (Explicit Range as Formal Parameter)
      ''''''''''''''''''''''''''''''''''''''''''''
      vArray = CreateFilteredArray(Sheets("Store").Range("A4:G90"), 2, 7)
    
      'Output the array contents in the Immediate Window (CTRL G in the debugger)
      Debug.Print "vArray output created on " & Now()
      If IsEmpty(vArray) = True Then
        Debug.Print "CreateFilteredArray() had bad input data - vArray is EMPTY."
      Else
        For i = LBound(vArray, 2) To UBound(vArray, 2)
          Debug.Print i, vArray(1, i), vArray(2, i)
        Next i
      End If
      Debug.Print
      
      
      
      ''''''''''''''''''''''''''''''''''''''''''''
      'Create an Array of Valid Filtered Data Example (Range Object as Formal Parameter)
      ''''''''''''''''''''''''''''''''''''''''''''
      Set myRange = Sheets("Store").Range("A4:G60")
      vArray = CreateFilteredArray(myRange, 2, 7)
    
      'Output the array contents in the Immediate Window (CTRL G in the debugger)
      Debug.Print "vArray output created on " & Now()
      If IsEmpty(vArray) = True Then
        Debug.Print "CreateFilteredArray() had bad input data - vArray is EMPTY."
      Else
        For i = LBound(vArray, 2) To UBound(vArray, 2)
          Debug.Print i, vArray(1, i), vArray(2, i)
        Next i
      End If
      Debug.Print
        
      
      'Clear object pointers
      Set myRange = Nothing
    
    End Sub
    Lewis
    Last edited by LJMetzger; 07-28-2015 at 11:45 AM.

  13. #13
    Forum Expert romperstomper's Avatar
    Join Date
    08-13-2008
    Location
    East Sussex, UK
    MS-Off Ver
    365, varying versions/builds
    Posts
    21,283

    Re: Create an array for filtered and non-contiguous columns

    Just FYI, you can late bind that function from cyiangou so you don't need the MS Forms reference each time:
    Public Function GetFilteredArray(FilterSheet As Excel.Worksheet, ParamArray ColIndex()) As Variant
        'ColIndex - list of column indexes you want returned, 1=first col
        Dim DataObj As Object
        Dim Rows As Variant, Cols As Variant
        Dim r  As Long, c As Long, v As Variant
        FilterSheet.AutoFilter.Range.Copy
        Set DataObj = GetObject("New:{1C3B4210-F441-11CE-B9EA-00AA006B1A69}")
        DataObj.GetFromClipboard
        Rows = Split(DataObj.GetText, vbCrLf)
        DataObj.Clear
        ReDim v(0 To UBound(Rows), 0 To UBound(ColIndex))
        For r = 0 To UBound(Rows)
            If Rows(r) = "" Then Exit For
            Cols = Split(Rows(r), vbTab)
            For c = 0 To UBound(ColIndex)
                v(r, c) = Cols(ColIndex(c) - 1)
            Next c
        Next r
        GetFilteredArray = v
    End Function
    Remember what the dormouse said
    Feed your head

  14. #14
    Forum Expert
    Join Date
    01-23-2013
    Location
    USA
    MS-Off Ver
    Microsoft 365 aka Office 365
    Posts
    3,863

    Re: Create an array for filtered and non-contiguous columns

    YasserKhalil wrote: ... beginner users use the UDF ...
    This post is only peripherally related to the thread.

    UDFs or User Defined Functions:
    Reference: http://www.ozgrid.com/VBA/Functions.htm#UDFIntro
    Simple Tutorial Reference: http://excelvbatutor.com/index.php/excel-vba-lesson-11/

    UDFs are a subset of VBA functions (and Subs).

    Some UDF Rules:
    a. Must be in an Ordinary Code Module (e.g. Module1) and can't be in a Sheet Module.
    b. Can't change the structure of a SpreadSheet.
    c. Can't change the physical characteristics of a cell (font, color).
    d. Can only implicitly (using the return value) change the value of the cell in which the UDF resides.
    e. Can be called from VBA.

    Some simple UDF examples:
    Function Square1(myCell As Range) As Double
      'This will create the square of 1 number
      'Formula Typical Usage:    =Square1(G29)
      'VBA     Typical Usage:  z = Square1(Range("G29"))
      
      Dim x As Double
      x = myCell.Value
      
      'A line that contains the name of the User Defined Function
      'sets the value of the Underlying Cell
      Square1 = x * x
      
    End Function
    
    Function Square1A(myCell As Range) As Double
      'This will return z value of ZERO because there is no 'Square1A' on the left side of a statement
      'Formula Typical Usage:    =Square1A(G29)
      'VBA     Typical Usage:  z = Square1A(Range("G29"))
      
      Dim x As Double
      Dim xSquared As Double
      
      x = myCell.Value
      xSquared = x * x
      
      'NOTE: This example is NO GOOD - because there is NO return value (i.e. 'Square1A = xSquared' is missing
      
    End Function
    
    Function Square2(x As Double) As Double
      'This will create the square of 1 number
      'Formula Typical Usage:     =Square2(G29) or =Square2(3) or = Square2(3.3)
      'VBA     Typical Usage:   z = Square2(Range("G29"))or z = Square2(3)
      
      Square2 = x * x
    
    End Function
    
    Function Square3NoGood(x As Double) As Double
      'This function is no good in a cell but will work in VBA
      'A User Defined Function cannot EXPLICITLY set the value of a cell
      
      [j111] = x * x
      Square3NoGood = x * x
    End Function
    
    Function Square4NoGood(x As Double) As Double
      'This function is no good in a cell but will work in VBA
      'A User Defined Function cannot EXPLICITLY set the value of a cell
      
      ActiveCell.Value = x * x
      Square4NoGood = x * x
      
    End Function
    
    Function Square5NoGood(x As Double) As Double
      'This function is no good in a cell but will work in VBA
      'A User Defined Function cannot EXPLICITLY set the value of a cell
      
      ActiveCell.Offset(0, 1).Value = x * x
      Square5NoGood = x * x
      
    End Function
    VBA code to test the above UDFs:
    Sub TestUDFs()
      'Test this function using Single Step (f8 or 'SHIFT f8' in debugger) and put cursor over value 'z'
    
      Dim rCell As Range
    
      Dim z As Double
      
      z = Square1(Range("G29"))
      
      Set rCell = Range("G43")
      z = Square1(rCell)
      Set rCell = Nothing
    
      z = Square1A(Range("G29"))    'ZERO expected value
    
      z = Square2(Range("G29"))
      z = Square2(3)
      z = Square2(1.2)
      
      z = Square3NoGood(3)          'Function no good in a cell but will work in VBA
      z = Square4NoGood(4)          'Function no good in a cell but will work in VBA
      z = Square5NoGood(5)          'Function no good in a cell but will work in VBA
      
    End Sub
    Lewis

  15. #15
    Forum Expert romperstomper's Avatar
    Join Date
    08-13-2008
    Location
    East Sussex, UK
    MS-Off Ver
    365, varying versions/builds
    Posts
    21,283

    Re: Create an array for filtered and non-contiguous columns

    Just for completeness, it is possible to work around b, c and d and enable a UDF to do pretty much anything a Sub can do, but it's complicated (either requiring Windows timers or using event code as well) and not usually a good idea.

+ Reply to Thread

Thread Information

Users Browsing this Thread

There are currently 1 users browsing this thread. (0 members and 1 guests)

Similar Threads

  1. Copy contiguous columns and paste as non-contiguous
    By absconditus in forum Excel General
    Replies: 1
    Last Post: 12-29-2014, 03:17 AM
  2. Array formula to create list of filtered results
    By Kris_cs1 in forum Excel Formulas & Functions
    Replies: 6
    Last Post: 05-16-2014, 02:34 AM
  3. Using non-contiguous cell references in an array
    By phook01 in forum Excel Formulas & Functions
    Replies: 22
    Last Post: 03-26-2014, 03:10 PM
  4. [SOLVED] How to sum non-contiguous columns applied as a formula on contiguous cells
    By figo12 in forum Excel Formulas & Functions
    Replies: 9
    Last Post: 10-09-2013, 01:07 PM
  5. Populate 2D-array with non-contiguous range
    By Laksefar in forum Excel Programming / VBA / Macros
    Replies: 0
    Last Post: 01-11-2013, 06:37 AM
  6. Copy and Paste an array (contiguous & non contiguous ranges)
    By Xrull in forum Excel Programming / VBA / Macros
    Replies: 5
    Last Post: 05-09-2010, 09:17 AM
  7. [SOLVED] Copying non-contiguous columns to contiguous columns
    By Bob in forum Excel Programming / VBA / Macros
    Replies: 1
    Last Post: 08-09-2006, 10:54 PM

Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts

Search Engine Friendly URLs by vBSEO 3.6.0 RC 1