2
\$\begingroup\$

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 x is an object or a unidimensional array, we simply use the first index to extract v = x(i1). Then we recurse on v using the other indices: Index(v, Array(i2, i3, ..., in)).
  • But if x is an array with r > 1 dimensions, we use the first r indices to extract v = x(i1, i2, i3, ..., ir). Then we recurse on v using 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!

  1. Nomenclature: Currently we have Arr_Index() for arrays; along with Index() and Index0() for arbitrary structures. But "Index" clashes with INDEX() in Excel, and possibly with WorksheetFunction.Index() in VBA.

    Now this module is inspired by pluck() in R. So should we rename Index*() as Pluck*(), and Arr_Index*() as Arr_Pluck*()? This should also entail renaming the entire Idx.bas module and indeed the Idx repository.

  2. Diagnostic Messaging: Currently the *Index() functions do not actually throw the validation errors they encounter, and I currently use Debug.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 standard Error 9. What is best practice for handling validation errors?

  3. 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 a Property...

    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 a Collection is read-only, so we cannot rely on the x(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 a Collection, once again this is not a generic implementation for objects in general. As for arrays, we can only resize them with a ReDim Preserve which might prove prohibitively costly; and multidimensional arrays add yet another layer of complexity.

  4. 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 Collection or 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?

  5. pluck() Features: The pluck() family has further attractive features, like testing with pluck_exists() whether the element actually exists, and having pluck() safely return a .default (like NULL) when it does not. As it stands, our *Index() functions actually bear more resemblance to chuck(), which throws ("chucks") an error rather than defaulting.

    Should we include a default argument to *Index(), so it better resembles pluck()? The Null value in VBA corresponds nicely to NULL in R. However, this would obviously complicate any assignment feature, because the Property signatures 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 Property
    
  6. Efficient Assignment: As discussed above under Lean Indices and Lean Assignment, our IndexRaw() extracts the element from any multidimensional array, and assigns it to v as 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 If
    

    But while this looks elegant, would it be more performant to avoid that intermediate step? If so, we must rework Arr_IndexRaw() as a Function.

     ' 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 If
    
  7. Trivial Indices: When u < l and there are no indices, then Arr_IndexRaw() handles this with a Case 0 and a Case 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 Select
    

    But running out of indices is a rare situation, and it only happens once per extraction. So this Case 0 does 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 removing Case 0, and letting the logic "fall through" to Case 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
asked Oct 22 at 21:14
\$\endgroup\$
5
  • \$\begingroup\$ Please edit your question so that the title describes the purpose of the code, rather than its mechanism. We really need to understand the motivational context to give good reviews. It's best to describe what value this code provides to its user. \$\endgroup\$ Commented Oct 23 at 10:17
  • \$\begingroup\$ @TobySpeight Well, "indexing into nested data structures" is the purpose of my code, while my recursive algorithm is the mechanism. The documentation for pluck() phrases its purpose similarly: "index deeply and flexibly into data structures". I have edited my overview to reflect this. \$\endgroup\$ Commented Oct 23 at 17:22
  • 1
    \$\begingroup\$ I see - thank you for the clarification. :-) \$\endgroup\$ Commented Oct 24 at 6:55
  • \$\begingroup\$ The questions are useful, and I get that you want feedback on the overall API/architecture, but you need to post the actual source code as part of your question in case the link dies (that would render reviews useless for future readers), and if you want reviewers to "pose and address additional questions" then people need to see the source code :) \$\endgroup\$ Commented Oct 27 at 12:35
  • \$\begingroup\$ @Greedo Thanks for the advice! I have edited my post to include the source code. I'm not quite sure what you mean by the link "dying", because it is tagged to a specific (pre)release on GitHub, but this update should forestall any issues on SO. 😊 \$\endgroup\$ Commented Oct 28 at 15:54

0

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.