How to implement this function?
Public Function ArraySlice(arr As Variant, dimension as Long, index as Long) As Variant 'Implementation here End Function
Suppose I need a fragment of an array. I specify the array, dimension and index for this dimension for which I want a slice.
As a specific example, suppose I have the following 5x4 2D array
0 1 2 3 4 ______________ 0| 1 1 2 3 1 1| 3 4 2 1 5 2| 4 5 3 2 6 3| 3 5 2 1 3
If the horizontal size is 1 and the vertical is 2, the return value of ArraySlice(array, 1, 3) will be a 1x4 2D array. Selected dimension 2 was flattened, and the only remaining values ββwere those that were originally at index 3 in size 2:
0 ____ 0| 3 1| 1 2| 2 3| 1
How do you implement this in VBA? The only implementations I can think of will include CopyMemory, unless I limit the number of valid and hardcoded sizes in each case.
NOTE: Here is how I can get the dimensions of the array.
UPDATE
Here are some more examples of the operation.
For a 2D array
0 1 2 3 4 ______________ 0| 1 1 2 3 1 1| 3 4 2 1 5 2| 4 5 3 2 6 3| 3 5 2 1 3
The result of ArraySlice(array, 2, 2) will be
0 1 2 3 4 ______________ 0| 4 5 3 2 6
Suppose I had a 3x3x3 matrix consisting of the following 2 dimensional slices, this example was modified to make it clearer
0 1 2 0 1 2 0 1 2 0 _________ 1 _________ 2 _________ 0| 1 1 1 0| 4 4 4 0| 7 7 7 1| 2 2 2 1| 5 5 5 1| 8 8 8 2| 3 3 3 2| 6 6 6 2| 9 9 9
(built like that)
Dim arr() As Long ReDim arr(2, 2, 2) arr(0, 0, 0) = 1 arr(1, 0, 0) = 1 arr(2, 0, 0) = 1 arr(0, 1, 0) = 2 arr(1, 1, 0) = 2 arr(2, 1, 0) = 2 arr(0, 2, 0) = 3 arr(1, 2, 0) = 3 arr(2, 2, 0) = 3 arr(0, 0, 1) = 4 arr(1, 0, 1) = 4 arr(2, 0, 1) = 4 arr(0, 1, 1) = 5 arr(1, 1, 1) = 5 arr(2, 1, 1) = 5 arr(0, 2, 1) = 6 arr(1, 2, 1) = 6 arr(2, 2, 1) = 6 arr(0, 0, 2) = 7 arr(1, 0, 2) = 7 arr(2, 0, 2) = 7 arr(0, 1, 2) = 8 arr(1, 1, 2) = 8 arr(2, 1, 2) = 8 arr(0, 2, 2) = 9 arr(1, 2, 2) = 9 arr(2, 2, 2) = 9
(dimensions are used in the mathematical sense of x, y, z, as opposed to the meaning of rows / columns)
The result of ArraySlice(array, 3, 1) will be a 3x3x1 array
0 1 2 0 _________ 0| 4 4 4 1| 5 5 5 2| 6 6 6
The result of ArraySlice(array, 2, 2) will be a 3x1x3 array
0 1 2 0 1 2 0 1 2 0 _________ 1 _________ 2 _________ 0| 3 3 3 0| 6 6 6 0| 9 9 9
UPDATE2
For DavidZemens, here is an example that will make it easier to track the elements involved:
For a 3x3x3 array constructed this way
Dim arr() As Long ReDim arr(2, 2, 2) arr(0, 0, 0) = "000" arr(1, 0, 0) = "100" arr(2, 0, 0) = "200" arr(0, 1, 0) = "010" arr(1, 1, 0) = "110" arr(2, 1, 0) = "210" arr(0, 2, 0) = "020" arr(1, 2, 0) = "120" arr(2, 2, 0) = "220" arr(0, 0, 1) = "001" arr(1, 0, 1) = "101" arr(2, 0, 1) = "201" arr(0, 1, 1) = "011" arr(1, 1, 1) = "111" arr(2, 1, 1) = "211" arr(0, 2, 1) = "021" arr(1, 2, 1) = "121" arr(2, 2, 1) = "221" arr(0, 0, 2) = "001" arr(1, 0, 2) = "102" arr(2, 0, 2) = "202" arr(0, 1, 2) = "012" arr(1, 1, 2) = "112" arr(2, 1, 2) = "212" arr(0, 2, 2) = "022" arr(1, 2, 2) = "122" arr(2, 2, 2) = "222"
The result of ArraySlice(array, 3, 1) will be a 3x3x1 array
0 1 2 0 ___________________ 0| "001" "101" "201" 1| "011" "111" "211" 2| "021" "121" "221"
FINAL UPDATE
Here is the complete solution - you can assume that the array functions are implemented as @GSerg suggests in the accepted answer. I decided that it makes sense to completely smooth the sliced ββsize, so if the slice of the 3x3x3 array (βcubeβ) is 3x1x3, it is smoothed to 3x3. I still have to solve the case when using this method, smoothing a 1-dimensional array will give a 0-dimensional array.
Public Function ArraySlice(arr As Variant, dimension As Long, index As Long) As Variant 'TODO: Assert that arr is an Array 'TODO: Assert dimension is valid 'TODO: Assert index is valid Dim arrDims As Integer arrDims = GetArrayDim(arr) 'N dimensions Dim arrType As Integer arrType = GetArrayType(arr) Dim zeroIndexedDimension As Integer zeroIndexedDimension = dimension - 1 'Make the dimension zero indexed by subtracting one, for easier math Dim newArrDims As Integer newArrDims = arrDims - 1 'N-1 dimensions since we're flattening "dimension" on "index" Dim arrDimSizes() As Variant Dim newArrDimSizes() As Variant ReDim arrDimSizes(0 To arrDims - 1) ReDim newArrDimSizes(0 To newArrDims - 1) Dim i As Long For i = 0 To arrDims - 1 arrDimSizes(i) = UBound(arr, i + 1) - LBound(arr, i + 1) + 1 Next 'Get the size of each corresponding dimension of the original For i = 0 To zeroIndexedDimension - 1 newArrDimSizes(i) = arrDimSizes(i) Next 'Skip over "dimension" since we're flattening it 'Get the remaining dimensions, off by one For i = zeroIndexedDimension To arrDims - 2 newArrDimSizes(i) = arrDimSizes(i + 1) Next Dim newArray As Variant newArray = CreateArray(arrType, newArrDims, newArrDimSizes) 'Iterate through dimensions, copying Dim arrCurIndices() As Variant Dim newArrCurIndices() As Variant ReDim arrCurIndices(0 To arrDims - 1) ReDim newArrCurIndices(0 To newArrDims - 1) arrCurIndices(zeroIndexedDimension) = index 'This is the slice Do While 1 'Copy the element PutArrayElement newArray, GetArrayElement(arr, arrCurIndices), newArrCurIndices 'Iterate both arrays to the next position If Not IncrementIndices(arrCurIndices, arrDimSizes, zeroIndexedDimension) Then 'If we've copied all the elements Exit Do End If IncrementIndices newArrCurIndices, newArrDimSizes Loop ArraySlice = newArray End Function Private Function IncrementIndices(arrIndices As Variant, arrDimensionSizes As Variant, Optional zeroIndexedDimension As Integer = -2) As Boolean 'IncrementArray iterates sequentially through all valid indices, given the sizes in arrDimensionSizes 'For example, suppose the function is called repeatedly with starting arrIndices of [0, 0, 0] and arrDimensionSizes of [3, 1, 3]. 'The result would be arrIndices changing as follows: '[0, 0, 0] first call '[0, 0, 1] '[0, 0, 2] '[1, 0, 0] '[1, 0, 1] '[1, 0, 2] '[2, 0, 0] '[2, 0, 1] '[2, 0, 2] 'The optional "dimension" parameter allows a dimension to be frozen and not included in the iteration. 'For example, suppose the function is called repeatedly with starting arrIndices of [0, 1, 0] and arrDimensionSizes of [3, 3, 3] and dimension = 2 '[0, 1, 0] first call '[0, 1, 1] '[0, 1, 2] '[1, 1, 0] '[1, 1, 1] '[1, 1, 2] '[2, 1, 0] '[2, 1, 1] '[2, 1, 2] Dim arrCurDimension As Integer arrCurDimension = UBound(arrIndices) 'If this dimension is "full" or if it is the frozen dimension, skip over it looking for a carry While arrIndices(arrCurDimension) = arrDimensionSizes(arrCurDimension) - 1 Or arrCurDimension = zeroIndexedDimension 'Carry arrCurDimension = arrCurDimension - 1 If arrCurDimension = -1 Then IncrementIndices = False Exit Function End If Wend arrIndices(arrCurDimension) = arrIndices(arrCurDimension) + 1 While arrCurDimension < UBound(arrDimensionSizes) arrCurDimension = arrCurDimension + 1 If arrCurDimension <> zeroIndexedDimension Then arrIndices(arrCurDimension) = 0 End If Wend IncrementIndices = True End Function