Project
I have created a VBA module called Idx which you may find here on GitHub. It is inspired by pluck() and chuck() in R, whose purpose is
to index deeply and flexibly into data structures.
It takes a set of indices and extracts the corresponding element from an arbitrary data structure. Indices may be numeric positions and textual keys, which you may supply as separate arguments or within a single array.
The data structure may be an array of any dimensionality, or a Collection, or a Dictionary...or even a deep nesting of all such structures. The only requirement is that the x(i) syntax applies at every level, like arrays or objects with a default member.
For your convenience, this post is broken down into four further sections:
- API for available features.
- Approach for details on implementation.
- Questions for specific issues where I desire advice.
- Code for the actual source code.
API
Here are the functions exposed by Idx, which are usable in VBA and (often) Excel. Here ... is a ParamArray where you supply indices as separate arguments.
' ######################
' ## Array Extraction ##
' ######################
' Supply indices programmatically.
Arr_Index(arr, indices)
' ##########################
' ## Arbitrary Extraction ##
' ##########################
' Supply indices programmatically...
Index(x, indices)
' ...and manually.
Index0(x, ...)
' ###############
' ## Utilities ##
' ###############
' Assign any value to a variable
Assign var, val
' Count the dimensions in an array.
Arr_Rank(arr)
' Measure an array along a dimension.
Arr_Length(arr, [dimension])
Approach
Array Extraction
The Arr_Index() function lets you access arrays of any dimensionality, using an array of positional indices:
Dim arr(1 To 2, 3 To 4, 5 To 6) As String
' ...
Arr_Index(arr, Array(2, 4, 5))
It does so by implementing a pyramid which handles every dimensionality, from one dimension through sixty dimensions. See here for source code.
Case 1: Assign v, a(i(l))
Case 2: Assign v, a(i(l),i(l+1))
Case 3: Assign v, a(i(l),i(l+1),i(l+2))
' ...
Case 60: Assign v, a(i(l),i(l+1),i(l+2),...,i(l+59))
Arbitrary Extraction
The Index() function lets you access any structure whatsoever, using an array of keyed or positional indices:
Dim clx As Collection
clx.Add arr, key := "3D Array"
Index(clx, 1, 2, 4, 5)
Index(clx, "3D Array", 2, 4, 5)
It does so with a recursive algorithm:
- If the input
xis an object or a unidimensional array, we simply use the first index to extractv = x(i1). Then we recurse onvusing the other indices:Index(v, Array(i2, i3, ..., in)). - But if
xis an array withr > 1dimensions, we use the firstrindices to extractv = x(i1, i2, i3, ..., ir). Then we recurse onvusing the remaining indices:Index(v, Array(ir+1, ir+2, ir+3, ..., in)).
This continues until we exhaust all n indices, at which point the value v is our result.
Lean Indices
The process of "slicing" an array typically involves loops and copying, which are inefficient here. Rather than splitting off two separate arrays at the rth index, we simply "reframe" the original array of indices, by adjusting the lower and upper bounds for our "window" of interest.
Consider an input x which is an r-dimensional array; and an array i of indices, ranging from the lower bound l to the upper bound u. Per the algorithm, we want the first r indices from i1 = i(l) through ir = i(k), where k = l + r - 1 marks the rth index.
So we pass the original indices i by reference, and simply specify a new window thereon, ranging from l through k. See here for source code.
' Extract the value from the array at those indices.
Arr_Index x, v := v, i := i, l := l, u := k
' ^ ^ ^
' ^ ^^^^^^^^^
' indices window
As for the remaining indices, we do likewise and specify another window from k + 1 through u.
' Index (recursively) into that value using any further indices.
If k < u Then
Index v, v := v, i := i, l := k + 1, u := u
' ^ ^^^^^ ^
' ^ ^^^^^^^^^^^^^
' indices window
End If
Lean Assignment
Because our data structures may contain both scalar and objective elements, we must replace the static syntax...
' Scalar.
v = x(i1)
' Object.
Set v = x(i1)
...with the utility Assign(), which flexibly assigns any value to a variable. See here for source code.
Assign v, x(i1)
But when we are many levels deep, it would be inefficient for a recursive Function like Index() to Assign() a return value. At m levels deep we would require m - 1 such Assign() operations, where each returns the recursive result to its caller:
Assign Index, Index(v, i = i, l := ..., u := ...)
So rather than using a Function, we implement a Subroutine called IndexRaw(). This accepts the reference to our variable v, and only when it reaches "bottom" does it Assign() the result to v. See here for source code.
' Base case: only one index left.
Else
Assign v, x(i(l))
End If
Lean Delegation
Generally speaking, every Function of the form *Index() has an *IndexRaw(): a lean Subroutine, which assigns the bottommost result to v. So just as Index() has an IndexRaw(), our Arr_Index() has an Arr_IndexRaw().
Every *Index() takes input and validates it once and for all, before delegating to *IndexRaw(). The latter does the heavy lifting of recursion—but skips validation to avoid redundant steps that burden performance. For the same reason, IndexRaw() actually passes arrays to Arr_IndexRaw() rather than Arr_Index().
Questions
I have already detailed most of these as issues on GitHub. Feel free to pose and address additional questions of your own!
Nomenclature: Currently we have
Arr_Index()for arrays; along withIndex()andIndex0()for arbitrary structures. But "Index" clashes withINDEX()in Excel, and possibly withWorksheetFunction.Index()in VBA.Now this module is inspired by
pluck()in R. So should we renameIndex*()asPluck*(), andArr_Index*()asArr_Pluck*()? This should also entail renaming the entireIdx.basmodule and indeed the Idx repository.Diagnostic Messaging: Currently the
*Index()functions do not actually throw the validation errors they encounter, and I currently useDebug.Print()as a placeholder.My instinct is to implement my own "error throwers" like
Errs_ArrayIsUninitialized(), which throw detailed errors with custom codes, rather than (say) a standardError 9. What is best practice for handling validation errors?Assignment: The
pluck()function also allows assignment:pluck(x, ...) <- value. Should I similarly implement "setters" to complement "getters" like*Index()? This is best done as aProperty...Property Get Arr_Index(arr, indices) ' ... End Property Property Let Arr_Index(arr, indices, value) ' ... End Property Property Set Arr_Index(arr, indices, value) ' ... End Property...but the process is daunting for
Index(), which sets a value deeply within an arbitrary structure. Now an object may be modified by reference, and anything that wraps it (as a reference) is updated accordingly. But when the target is not an object, and it is nested within an array, then we must first modify the target and then overwrite the array location with our result.And even objects have their complications! By assumption our objects have a default method for getting values, so the
x(i)syntax always applies for getting. But the.Item()for aCollectionis read-only, so we cannot rely on thex(i) = ...syntax. Indeed, the workaround can be convoluted, and there is no generic implementation for objects in general.Finally, the
pluck()assignment creates a new "slot" when it does not already exist. While we can easily.Add()to aCollection, once again this is not a generic implementation for objects in general. As for arrays, we can only resize them with aReDim Preservewhich might prove prohibitively costly; and multidimensional arrays add yet another layer of complexity.Multiple Extraction: So far we require each index to be either a numeric (
Long) position or a textual (String) key. But if an individual index is instead an array, then perhaps this should trigger iteration, so that a single call of this form...Index(x, Array(5, Array(2, "a", 4), "x")) ' ^^^^^^^^^^^^^^^^...is equivalent to several calls of this form...
Index(x, Array(5, 2, "x") Index(x, Array(5, "a", "x") Index(x, Array(5, 4, "x")...where the results are gathered (into a
Collectionor array) and returned. This will be quite challenging for multidimensional arrays. Is this feature worth the effort, as a sufficiently logical extension of the existing framework?pluck()Features: Thepluck()family has further attractive features, like testing withpluck_exists()whether the element actually exists, and havingpluck()safely return a.default(likeNULL) when it does not. As it stands, our*Index()functions actually bear more resemblance tochuck(), which throws ("chucks") an error rather than defaulting.Should we include a
defaultargument to*Index(), so it better resemblespluck()? TheNullvalue in VBA corresponds nicely toNULLin R. However, this would obviously complicate any assignment feature, because thePropertysignatures would not match properly:Property Get Arr_Index(arr, indices, Optional default As Variant = Null) ' ... End Property Property Let Arr_Index(arr, indices, value) ' ... End PropertyEfficient Assignment: As discussed above under Lean Indices and Lean Assignment, our
IndexRaw()extracts the element from any multidimensional array, and assigns it tovas a placeholder before recursing further thereon. See here for source code.' Extract the value from the array at those indices. Arr_IndexRaw x, v := v, i := i, l := l, u := k ' Index (recursively) into that value using any further indices. If k < u Then IndexRaw v, v := v, i := i, l := k + 1, u := u End IfBut while this looks elegant, would it be more performant to avoid that intermediate step? If so, we must rework
Arr_IndexRaw()as aFunction.' Target the element from array at those indices: either extract its value as is... If Not k < u Then Assign v, Arr_IndexRaw(x, i := i, l := l, u := k) ' ...or index (recursively) into it using further indices. Else IndexRaw Arr_IndexRaw(x, i := i, l := l, u := k), v := v, i := i, l := k + 1, u := u ' ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ End IfTrivial Indices: When
u < land there are no indices, thenArr_IndexRaw()handles this with aCase 0and aCase Else. See here for source code.Select Case Case 0: Assign v, a() Case 1: Assign v, a(i(l)) Case 2: Assign v, a(i(l),i(l+1)) Case 3: Assign v, a(i(l),i(l+1),i(l+2)) ' ... Case 60: Assign v, a(i(l),i(l+1),i(l+2),...,i(l+59)) Case Else: Assign v, a() End SelectBut running out of indices is a rare situation, and it only happens once per extraction. So this
Case 0does save us 60 checks in this situation, but it also adds one extra check in every situation where indices are present. Would we achieve better performance by removingCase 0, and letting the logic "fall through" toCase Else?
Code
Here is the source code for version v0.1.0 of my Idx.bas module.
Attribute VB_Name = "Idx"
' #############
' ## Options ##
' #############
' Explicitly declare all variables.
Option Explicit
' ' Hide these developer functions from end users in Excel.
' Option Private Module
' ##############
' ## Metadata ##
' ##############
Public Const MOD_NAME As String = "Idx"
Public Const MOD_VERSION As String = "0.1.0"
Public Const MOD_REPO As String = "https://github.com/GregYannes/Idx"
' ###############
' ## Constants ##
' ###############
' The most dimensions an array may have.
Private Const MAX_ARR_RANK As Long = 60
' #########
' ## API ##
' #########
' Manually extract a value (by index) from an arbitrary data structure.
Public Function Index0(ByRef x As Variant, _
ParamArray indices() As Variant _
) As Variant
Dim i As Variant: i = indices
Assign Index0, Index(x, indices := i)
End Function
' Programmatically extract a value (by index) from an arbitrary data structure.
Public Function Index(ByRef x As Variant, _
ByRef indices As Variant _
) As Variant
' ################
' ## Validation ##
' ################
' Short-circuit for no array.
If Not VBA.IsArray(indices) Then Debug.Print "ERROR: The indices must be an array."
' Short-circuit for uninitialized...
Dim iRnk As Long: iRnk = Arr_Rank(indices)
If iRnk = 0 Then
Debug.Print "ERROR: The indices must be initialized."
' ...or multidimensional indices.
ElseIf iRnk > 1 Then
Debug.Print "ERROR: The indices must have exactly one dimension."
End If
' Short-circuit for no indices.
Dim iLen As Long: iLen = Arr_Length(indices, dimension := 1)
If iLen = 0 Then
Debug.Print "ERROR: At least one index is required."
End If
' ################
' ## Extraction ##
' ################
' Record the bounds of the indices.
Dim low As Long: low = LBound(indices, 1)
Dim up As Long: up = UBound(indices, 1)
' Index dynamically into the structure and extract the value there.
IndexRaw x, v := Index, i := indices, l := low, u := up
End Function
' Programmatically extract a value (by index) from a multidimensional array.
Public Function Arr_Index(ByRef arr As Variant, _
ByRef indices As Variant _
) As Variant
' ################
' ## Validation ##
' ################
' Short-circuit for no arrays.
If Not VBA.IsArray(arr) Then Debug.Print "ERROR: Input must be an array."
If Not VBA.IsArray(indices) Then Debug.Print "ERROR: The indices must be an array."
' Short-circuit for uninitialized...
Dim iRnk As Long: iRnk = Arr_Rank(indices)
If iRnk = 0 Then
Debug.Print "ERROR: The indices must be initialized."
' ...or multidimensional indices.
ElseIf iRnk > 1 Then
Debug.Print "ERROR: The indices must have exactly one dimension."
End If
' Short-circuit for no indices...
Dim iLen As Long: iLen = Arr_Length(indices, dimension := 1)
If iLen = 0 Then
Debug.Print "ERROR: At least one index is required."
' ...or for impossibly many.
ElseIf iLen > MAX_ARR_RANK Then
Debug.Print "ERROR: No array may accept more than " & VBA.CStr(MAX_ARR_RANK) & " indices for " & VBA.CStr(MAX_ARR_RANK) & " dimensions."
End If
' Short-circuit for uninitialized array.
Dim aRnk As Long: aRnk = Arr_Rank(arr)
If aRnk = 0 Then Debug.Print "ERROR: The array must be initialized."
' Short-circuit for wrong number of indices.
If iLen <> aRnk Then Debug.Print "ERROR: There must be exactly as many indices (" & VBA.CStr(iLen) & ") as dimensions (" & VBA.CStr(aRnk) ") in the array."
' ################
' ## Extraction ##
' ################
' Record the bounds of the indices.
Dim low As Long: low = LBound(indices, 1)
Dim up As Long: up = UBound(indices, 1)
' Index dynamically into the array and extract the value there.
Arr_IndexRaw arr, v := Arr_Index, i := indices, l := low, u := up
End Function
' #############
' ## Support ##
' #############
' Lean workhorse for recursive extraction from arbitrary data structures.
Private Sub IndexRaw(ByRef x As Variant, _
ByRef v As Variant, _
ByRef i As Variant, _
ByVal l As Long, _
ByVal u As Long _
)
' Recursive case: several indices left.
If l < u Then
' Index into an array on all its dimensions.
If VBA.IsArray(x) Then
' Short circuit for uninitialized array.
Dim r As Long: r = Arr_Rank(x)
If r = 0 Then Debug.Print "ERROR: Uninitialized array."
' Demarcate the indices for this array.
Dim k As Long: k = l + r - 1
' Short-circuit for too few indices.
If k > u Then Debug.Print "ERROR: Too few indices for array."
' Extract the value from the array at those indices.
Arr_IndexRaw x, v := v, i := i, l := l, u := k
' Index (recursively) into that value using any further indices.
If k < u Then
IndexRaw v, v := v, i := i, l := k + 1, u := u
End If
' Index singly into any other structure.
Else
IndexRaw x(i(l)), v := v, i := i, l := l + 1, u := u
End If
' Base case: only one index left.
Else
Assign v, x(i(l))
End If
End Sub
' Lean workhorse for extraction from multidimensional arrays.
Private Sub Arr_IndexRaw(ByRef a As Variant, _
ByRef v As Variant, _
ByRef i As Variant, _
ByVal l As Long, _
ByVal u As Long _
)
Dim n As Long: n = u - l + 1
Select Case n
Case 0: Assign v, a()
Case 1: Assign v, a(i(l))
Case 2: Assign v, a(i(l),i(l+1))
Case 3: Assign v, a(i(l),i(l+1),i(l+2))
Case 4: Assign v, a(i(l),i(l+1),i(l+2),i(l+3))
Case 5: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4))
Case 6: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5))
Case 7: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6))
Case 8: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7))
Case 9: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8))
Case 10: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9))
Case 11: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10))
Case 12: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11))
Case 13: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12))
Case 14: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13))
Case 15: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14))
Case 16: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15))
Case 17: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16))
Case 18: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17))
Case 19: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18))
Case 20: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19))
Case 21: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20))
Case 22: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21))
Case 23: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22))
Case 24: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23))
Case 25: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24))
Case 26: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25))
Case 27: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26))
Case 28: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27))
Case 29: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28))
Case 30: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29))
Case 31: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30))
Case 32: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31))
Case 33: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32))
Case 34: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33))
Case 35: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34))
Case 36: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35))
Case 37: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36))
Case 38: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37))
Case 39: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38))
Case 40: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39))
Case 41: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40))
Case 42: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41))
Case 43: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42))
Case 44: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43))
Case 45: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44))
Case 46: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45))
Case 47: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46))
Case 48: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47))
Case 49: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48))
Case 50: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49))
Case 51: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50))
Case 52: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51))
Case 53: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52))
Case 54: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53))
Case 55: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54))
Case 56: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54),i(l+55))
Case 57: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54),i(l+55),i(l+56))
Case 58: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54),i(l+55),i(l+56),i(l+57))
Case 59: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54),i(l+55),i(l+56),i(l+57),i(l+58))
Case 60: Assign v, a(i(l),i(l+1),i(l+2),i(l+3),i(l+4),i(l+5),i(l+6),i(l+7),i(l+8),i(l+9),i(l+10),i(l+11),i(l+12),i(l+13),i(l+14),i(l+15),i(l+16),i(l+17),i(l+18),i(l+19),i(l+20),i(l+21),i(l+22),i(l+23),i(l+24),i(l+25),i(l+26),i(l+27),i(l+28),i(l+29),i(l+30),i(l+31),i(l+32),i(l+33),i(l+34),i(l+35),i(l+36),i(l+37),i(l+38),i(l+39),i(l+40),i(l+41),i(l+42),i(l+43),i(l+44),i(l+45),i(l+46),i(l+47),i(l+48),i(l+49),i(l+50),i(l+51),i(l+52),i(l+53),i(l+54),i(l+55),i(l+56),i(l+57),i(l+58),i(l+59))
Case Else: Assign v, a()
End Select
End Sub
' ###############
' ## Utilities ##
' ###############
' Assign a value (scalar or objective) to a variable.
Public Sub Assign( _
ByRef var As Variant, _
ByVal val As Variant _
)
If VBA.IsObject(val) Then
Set var = val
Else
var = val
End If
End Sub
' Get the length (along a dimension) of an array.
Public Function Arr_Length(ByRef arr As Variant, _
Optional ByVal dimension As Long = 1 _
) As Long
On Error GoTo BOUND_ERROR
Arr_Length = UBound(arr, dimension) - LBound(arr, dimension) + 1
Exit Function
BOUND_ERROR:
Arr_Length = 0
End Function
' Get the "rank" of an array: the count of its dimensions.
Public Function Arr_Rank(ByRef arr As Variant) As Long
Dim tst As Long
Arr_Rank = 0
On Error GoTo BOUND_ERROR
Do While True
Arr_Rank = Arr_Rank + 1
tst = UBound(arr, Arr_Rank)
Loop
BOUND_ERROR:
Arr_Rank = Arr_Rank - 1
End Function
pluck()phrases its purpose similarly: "index deeply and flexibly into data structures". I have edited my overview to reflect this. \$\endgroup\$:-)\$\endgroup\$