How to extract a 1D array from N-dimensional array?

Kelvin Stott

Active Member
Joined
Oct 26, 2010
Messages
338
Hello,

I have a N-dimensional array called "Data", where the number of dimensions N is variable...

How can I extract a 1-dimensional array from this, comprising all elements along the Xth dimension, which are the first element along all the other dimensions?

For example, this would be equivalent to taking the values along one edge of a cubic 3D array, by specifying one of the 3 dimensions as X.

Thanks indeed for any help.

Kelvin
 
Last edited:
Kelvin, Have you had any success running the code I suggested? It does exactly what you describe.

Here's a test sub you could use for a 3D array.

Code:
Sub Test3()
    Dim Data(1 To 2, 11 To 13, 1 To 2) As Variant
    Dim vEdge() As Variant

    Data(1, 11, 1) = "1A1"
    Data(1, 11, 2) = "1A2"
    Data(1, 12, 1) = "1B1"
    Data(1, 12, 2) = "1B2"
    Data(1, 13, 1) = "1C1"
    Data(1, 13, 2) = "1C2"
    
    Data(2, 11, 1) = "2A1"
    Data(2, 11, 2) = "2A2"
    Data(2, 12, 1) = "2B1"
    Data(2, 12, 2) = "2B2"
    Data(2, 13, 1) = "2C1"
    Data(2, 13, 2) = "2C2"

    vEdge = Extract1D(Data, 2)

'---display results in Immediate Window of VBE
    Dim i As Long
    
    For i = 1 To UBound(vEdge)
        Debug.Print vEdge(i)
    Next i
   
End Sub
 
Upvote 0

Excel Facts

Waterfall charts in Excel?
Office 365 customers have access to Waterfall charts since late 2016. They were added to Excel 2019.
That was my understanding of the OP question. The OP further said that each fixed index would be the first element (LBound) of that dimension.
For Tushar's example, the result of having X=2 would be a 1-Dim array having the values found at these indicies of the 3-Dim array.
0 1 0
0 2 0
0 3 0
Yes, exactly!
 
Upvote 0
For the 1st dimension (X=1) of a 3D array (N=3), the 1D array would be: [all, 1, 1]
For the 2nd dimension (X=2) of a 3D array (N=3), the 1D array would be: [1, all, 1]

For the 1st dimension (X=1) of a 4D array (N=4), the 1D array would be: [all, 1, 1, 1]
For the 3rd dimension (X=3) of a 4D array (N=4), the 1D array would be: [1, 1, all, 1]

For the 8th dimension (X=8) of a 24D array (N=24), the 1D array would be: [(1,)x7, all, (1,)x16]
That was my suggestion in post#11, for the general case in which the other dimensions can have any value, not just 1.
 
Upvote 0
Thanks all, now that I understand we're on the same page I'm going to go through all these suggestions more carefully. Apologies if I missed the answer earlier.
 
Upvote 0
Here's what I was suggesting, which is similar to Jerry's.

Code:
Sub Test()
    Dim i1          As Long
    Dim i2          As Long
    Dim i3          As Long
    Dim i4          As Long

    Dim av(0 To 2, 1 To 3, 2 To 4, 3 To 5) As Variant
    Dim avOut       As Variant

    For i1 = 0 To 2
        For i2 = 1 To 3
            For i3 = 2 To 4
                For i4 = 3 To 5
                    av(i1, i2, i3, i4) = i1 & i2 & i3 & i4
                Next i4
            Next i3
        Next i2
    Next i1

    avOut = SuperDuperIndex(av, 1, 2, Empty, 4)
    Stop    ' and look at avOut
End Sub

Function SuperDuperIndex(avInp As Variant, ParamArray aviDim() As Variant) As Variant
    ' aviDim is a 0-based array that specifies the fixed values for all but one
    ' dimension of avInp, and contains Empty for the free dimension
    
    ' Returns a 1D array having the same indexing as the free dimension of avInp
    Dim nDim        As Long
    Dim nOut        As Long
    Dim i           As Long
    Dim iLB         As Long
    Dim iUB         As Long     ' lower bound of free dimension
    Dim k           As Long     ' upper bound of free dimension
    Dim iFree       As Long     ' free dimension
    Dim avOut       As Variant
    Dim avi         As Variant  ' copy of aviDim

    ' need a copy so we can change the free index
    avi = aviDim
    nDim = UBound(avi) + 1

    For i = 0 To nDim - 1
        If IsEmpty(avi(i)) Then
            iFree = i + 1
            Exit For
        End If
    Next i

    iLB = LBound(avInp, iFree)
    iUB = UBound(avInp, iFree)
    ReDim avOut(iLB To iUB)

    Select Case nDim
        Case 2
            For i = iLB To iUB
                avi(iFree) = i
                avOut(i) = avInp(avi(1), avi(2))
            Next i
        Case 3
            For i = iLB To iUB
                avi(iFree) = i
                avOut(i) = avInp(avi(1), avi(2), avi(3))
            Next i
        Case 4
            For i = iLB To iUB
                avi(iFree - 1) = i
                avOut(i) = avInp(avi(0), avi(1), avi(2), avi(3))
            Next i
        Case 5
            For i = iLB To iUB
                avi(iFree - 1) = i
                avOut(i) = avInp(avi(0), avi(1), avi(2), avi(3), avi(4))
            Next i
        Case Else
            SuperDuperIndex = "Oops!"
    End Select

    SuperDuperIndex = avOut
End Function
 
Upvote 0
Hi

Just addressing the problem of the variable number of dimensions.

In case we don't know in advance the number of dimensions of an array, a way to work around the syntax is to copy the array to a 1D array.
With the 1D array we just have to use the 1D index to access any element. The just is in bold because the idea is simple but the execution involves some work, although we already know how to calculate the number of dimensions and the LBound and HBound of each dimension.

This way we avoid the case for the number of dimensions and it works for any number of dimensions.

In this example I use the LBound and HBound of each dimension for vArrND that I know, but that I would calculate otherwise.

Code:
Sub Test()
Dim vArrND(1 To 5, 1 To 6, 1 To 2, 1 To 4), vArr1D As Variant
Dim v As Variant
Dim j As Long

vArrND(1, 2, 2, 4) = "Hi"
vArrND(4, 3, 1, 1) = " there!"

' redim the 1D array
ReDim vArr1D(1 To 240)

' copy the elements of the n-dimensional array to the 1D array
For Each v In vArrND
    j = j + 1
    vArr1D(j) = v
Next v

MsgBox vArr1D(216) & vArr1D(14)

End Sub
 
Upvote 0
I'm glad it helped.

Notice that I was trying to circumvent the problem of addressing a specific element in a n-dimensional array when you don't know in advance how many dimensions it has.

For most real cases, where you don't have many dimensions, it may be simpler to use a Select Case like Jerry and Shg did.
 
Upvote 0
Nice.

It's simpler than the one I was considering and it suggests an approach that avoids the conversion to a 1D array.

Since it is possible to compute which elements of the 1D array are of interest, just pick them as they "roll by" in the 'for each v' loop -- no need to actually create the 1D array.

So, in your example,

If j=216 or j=14 then rslt=v & rslt

Obviously, in the more general case, the indices of interest would have to be stored in an array and the result organized so that the elements are in the correct order.

Also worth noting, and it is implicit in the code, is that VBA stores multi-dimension arrays in the reverse order of the dimensions. So, in your example of a 4D matrix, the allocation can be thought of as
There is 1 4D matrix.
Each element of the 4D matrix (indexed by 1 to 4) is a 3D matrix, resulting in 4 3D matrices
Each element of each 3D matrix (indexed by 1 to 2) is a 2D matrix, resulting in 4*2 or 8 2D matrices
Each element of each 2D matrixby (indexed by 1 to 6) is a 1D vector, resulting in 8*6 or 48 1D vectors
Each element of each 1D vector (indexed by 1 to 5) is a single element, resulting in 48*5 or 240 elements.

So, the 1D equivalent of the element 1,2,2,4 is (4-1)*(2*6*5)+(2-1)*(6*5)+(2-1)*5+1 = 180 + 30 + 5 + 1 = 216
Similarly, (4,3,1,1) becomes 0 + 0 + 2 * 5 + 4 = 14.
Hi

Just addressing the problem of the variable number of dimensions.

In case we don't know in advance the number of dimensions of an array, a way to work around the syntax is to copy the array to a 1D array.
With the 1D array we just have to use the 1D index to access any element. The just is in bold because the idea is simple but the execution involves some work, although we already know how to calculate the number of dimensions and the LBound and HBound of each dimension.

This way we avoid the case for the number of dimensions and it works for any number of dimensions.

In this example I use the LBound and HBound of each dimension for vArrND that I know, but that I would calculate otherwise.

Code:
Sub Test()
Dim vArrND(1 To 5, 1 To 6, 1 To 2, 1 To 4), vArr1D As Variant
Dim v As Variant
Dim j As Long

vArrND(1, 2, 2, 4) = "Hi"
vArrND(4, 3, 1, 1) = " there!"

' redim the 1D array
ReDim vArr1D(1 To 240)

' copy the elements of the n-dimensional array to the 1D array
For Each v In vArrND
    j = j + 1
    vArr1D(j) = v
Next v

MsgBox vArr1D(216) & vArr1D(14)

End Sub
 
Upvote 0

Forum statistics

Threads
1,216,101
Messages
6,128,842
Members
449,471
Latest member
lachbee

We've detected that you are using an adblocker.

We have a great community of people providing Excel help here, but the hosting costs are enormous. You can help keep this site running by allowing ads on MrExcel.com.
Allow Ads at MrExcel

Which adblocker are you using?

Disable AdBlock

Follow these easy steps to disable AdBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the icon in the browser’s toolbar.
2)Click on the "Pause on this site" option.
Go back

Disable AdBlock Plus

Follow these easy steps to disable AdBlock Plus

1)Click on the icon in the browser’s toolbar.
2)Click on the toggle to disable it for "mrexcel.com".
Go back

Disable uBlock Origin

Follow these easy steps to disable uBlock Origin

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back

Disable uBlock

Follow these easy steps to disable uBlock

1)Click on the icon in the browser’s toolbar.
2)Click on the "Power" button.
3)Click on the "Refresh" button.
Go back
Back
Top