And, when making a generic function, you also have to worry about the lower bound of the array. Most developers like using arrays that start off with element zero, but some developers set Option Base 1 in their script, and a generic function will have to account for that. Furthermore, there may be cases when it's more convenient to define an array with "strange" bounds, like Dim a(5 To 10). A generic function has to account for this.

Next, a decision has to be made when returning subsets from the right (a negative number). For example, let's say you have an array of elements 0, 1, 2, and 3. If you want to return the rightmost 2 elements (-2), you would be expecting the values from 2 and 3, but what positions are they in the returned array? Are they elements 0 and 1, or elements 2 and 3? We'll show code that does either, so you can decide which one to implement.

Lastly, we want to mimic @Subset as much as possible. If you ask for 15 elements in an array that has 4 elements, all 4 elements are returned. If you ask for 0 elements, an error is returned. We'll be nicer and return an empty string (which may or may not cause an error, depending on the calling function). And doing a quick test on @Subset reveals that if you don't pass in an array, the entire thing you passed in was returned. For example, @Subset("Hello"; 2) = "Hello". So we'll want to mimic that in our function.

Function Subset(array As Variant, n As Integer) As Variant

Dim retVal As Variant

Dim i As Integer

If Not Isarray(array) Then

Subset = array

Exit Function

End If

If n = 0 Then

Subset = ""

Exit Function

End If

If Abs(n) >= (Ubound(array) - Lbound(array) + 1) Then

Subset = array

Exit Function

End If

If n > 0 Then

Redim retVal(Lbound(array) To (Lbound(array) + n - 1)) As Variant

Else

Redim retVal((Ubound(array) - Abs(n) + 1) To Ubound(array)) As Variant

End If

For i = Lbound(retVal) To Ubound(retVal)

retVal(i) = array(i)

Next

Subset = retVal

End Function

The tricky part is defining the bounds of the returned array. If n > 0 then we define the return array to be the existing lower bound to the new upper bound (so the return array will have "n" elements). If n < 0 then the upper bound should be the same as what was passed in, and the lower bound should adjust so there will be "n" elements). Then just move everything over in the same position.

Note: The code above returns everything in the same position. So, this code snippet:

Dim arr(4) As String

Dim result As Variant

arr(0) = "A"

arr(1) = "B"

arr(2) = "C"

arr(3) = "D"

arr(4) = "E"

result = Subset(arr, -2)

will end up with result(3) = "D" and result(4) = "E".

If you would rather have that same code snippet end up with result(0) = "D" and result(1) = "E", then you'll need one more variable called adjust (an Integer) defined at the top of the function. Then the first three "If" blocks are the same and the rest of the code becomes:

Redim retVal(Lbound(array) To (Lbound(array) + Abs(n) - 1)) As Variant

For i = Lbound(retVal) To Ubound(retVal)

adjust = 0

If n < 0 Then

adjust = (Ubound(array) - Lbound(array) + 1) - Abs(n)

End If

retVal(i) = array(i+adjust)

Next

Subset = retVal

End Function

In this code, the lower bound is always going to be the same as what was passed in, and the upper bound is always going to adjust so there's "n" elements returned. But we have to adjust the position of the source element when placing it in the returned array. If n is greater than zero, then no adjustment is necessary. If n is less than zero, then we need to know how many elements, counting from the left, are not going to be used. That is the value of adjust.