1

In Google Sheets, I can use the ={range1;range2;...} notation to append multiple ranges into one as part of a formula. In Excel, it seems this functionality isn't available as a formula. I would like to create a user defined function to allow me to append ranges in Excel.

Ideally, the function would have a similar simplicity to the Google Sheets version. I've attached a link to a public Google Sheets document with a basic use case, in case it's helpful.

I've tried Tom's answer but it gives an error (see example photo).

Thanks!

player0
  • 124,011
  • 12
  • 67
  • 124
devlife
  • 13
  • 3
  • @ScottCraner `=CHOOSE({1,2},range1,range2)` seems to be creating a 3x3 array where the first column is from the first range, the second column from the second range, and the third column filled with #N/A errors. Is there a way to append the rows from my second range onto the rows from my first range? If I have two 3x3 source arrays, I want to end up with a 3x6 result array. Thanks! – devlife Aug 04 '21 at 16:58
  • 1
    thanks for your question. I think my answer should do what you want. I actually didn't know you could do what you showed with the semicolon in Google Sheets. Very cool. – pgSystemTester Aug 04 '21 at 17:14

2 Answers2

1

Here is a general UDF to stack ranges of the same number of columns:

Function vStack(ParamArray rng() As Variant) As Variant
    If TypeName(rng(1)) <> "Range" Then Exit Function
    Dim otarr() As Variant
    ReDim otarr(1 To 100000, 1 To rng(1).Columns.Count)
    
    Dim z As Long
    z = 1
    
    Dim i As Long
    For i = LBound(rng) To UBound(rng)
        If TypeName(rng(i)) <> "Range" Then Exit Function
        If i > LBound(rng) Then
            If rng(i).Columns.Count <> rng(i - 1).Columns.Count Then Exit Function
        End If
        Dim rngarr As Variant
        rngarr = Intersect(rng(i), rng(i).Parent.UsedRange)
        Dim j As Long
        For j = LBound(rngarr, 1) To UBound(rngarr, 1)
            Dim k As Long
            For k = LBound(rngarr, 2) To UBound(rngarr, 2)
                otarr(z, k) = rngarr(j, k)
            Next k
            z = z + 1
        Next j
    Next i
    
    Dim nArray() As Variant
    ReDim nArray(1 To z - 1, 1 To UBound(otarr, 2))
    
    For i = 1 To z - 1
        For j = 1 To UBound(otarr, 2)
            nArray(i, j) = otarr(i, j)
        Next j
    Next i
    
    vStack = nArray

End Function

One note, I limit the initial array to 100,000 rows. If this is not enough you can up that to what ever you want, but also think, "Am I treating Excel as a database?". If the answer is yes, it is time to make the switch to an actual referential database.

enter image description here

Then one can use it in a formula:

=FILTER(vStack(A:C,F:H),vStack(A:A,F:F)="Apples")

enter image description here


Edit to include a version that works with arrays ie: =vstack({1;2;3},{4;5;6})

Function vStack(ParamArray rng() As Variant) As Variant
    Dim otarr() As Variant
    If TypeName(rng(1)) = "Range" Then
        ReDim otarr(1 To 100000, 1 To rng(1).Columns.Count)
    Else
        ReDim otarr(1 To 100000, 1 To UBound(rng(1), 2))
    End If
        
    
    
    Dim z As Long
    z = 1
    
    Dim i As Long
    For i = LBound(rng) To UBound(rng)
        
        If i > LBound(rng) Then
            If TypeName(rng(i)) = "Range" Then
                If rng(i).Columns.Count <> UBound(otarr, 2) Then Exit Function
            Else
                If UBound(rng(i), 2) <> UBound(otarr, 2) Then Exit Function
            End If
        End If
        Dim rngarr As Variant
        If TypeName(rng(i)) = "Range" Then
            rngarr = Intersect(rng(i), rng(i).Parent.UsedRange)
        Else
            rngarr = rng(i)
        End If

        Dim j As Long
        For j = LBound(rngarr, 1) To UBound(rngarr, 1)
            Dim k As Long
            For k = LBound(rngarr, 2) To UBound(rngarr, 2)
                otarr(z, k) = rngarr(j, k)
            Next k
            z = z + 1
        Next j
    Next i
    
    Dim nArray() As Variant
    ReDim nArray(1 To z - 1, 1 To UBound(otarr, 2))
    
    For i = 1 To z - 1
        For j = 1 To UBound(otarr, 2)
            nArray(i, j) = otarr(i, j)
        Next j
    Next i
    
    vStack = nArray

End Function
Scott Craner
  • 148,073
  • 10
  • 49
  • 81
0

UPDATED With Dynamic Available Ranges

This function should create what you want. Note this does not take into account blanks or headers. See sample spreadsheet with working result.

Function combineRange(ParamArray theRanges() As Variant) As Variant()
    Dim totalColumns As Long, z As Long, r As Long, g As Long, aCell As Range
    
    ReDim zRanges(0) As Range
    For r = LBound(theRanges) To UBound(theRanges)
        ReDim Preserve zRanges(r)
        Set zRanges(r) = theRanges(r)
        Set zRanges(r) = Intersect(zRanges(r), zRanges(r).Worksheet.UsedRange)
       
        totalColumns = Application.WorksheetFunction.Max(zRanges(r).Columns.Count, totalColumns)
    Next r
    
    r = 1
    
    ReDim theRay(1 To totalColumns, 1 To r)
    
    For z = LBound(zRanges) To UBound(zRanges)
        For Each aCell In zRanges(z).Columns(1).Cells
           ReDim Preserve theRay(1 To totalColumns, 1 To r)
            For g = 1 To zRanges(z).Columns.Count
                theRay(g, r) = aCell.Offset(0, g - 1).Value
            Next g
            r = r + 1
        Next aCell
    Next z
    
    combineRange = Application.WorksheetFunction.Transpose(theRay)
End Function

enter image description here

pgSystemTester
  • 8,979
  • 2
  • 23
  • 49
  • Thank you!! This is the basic idea of what I'm looking for, but is there a way to make it accept more than two ranges? I used two in the question as an example, but what's nice about the Google Sheets syntax is that it lets you put as many ranges as you want. I apologize for the confusion, I should have been clearer in the question. Thanks again! – devlife Aug 04 '21 at 17:16
  • Yea you could use `ParamArray` to create unlimited arrays. Lookup that up regarding User Defined functions and I suspect you'll figure it out. If not, post another question. Do you mind clicking `accept` since this did address your question? Thanks and glad it works. – pgSystemTester Aug 04 '21 at 17:24
  • @PGSystemTester I would argue that the question was more broad than the specific example. The OP was just giving the filter formula as an example not the end case. The question was how to use `{range1;range2}` and get a single array that can be used in other formula like one can in sheets. – Scott Craner Aug 04 '21 at 17:46
  • Ok. I updated it to be dynamic. @devlife you now have a dynamic result for unlimited ranges... – pgSystemTester Aug 04 '21 at 17:53