开发者

Highest populated dimension of an array vba

Say i have an single dimension array (to keep it simple). Is there a simple way to tell what the 开发者_运维技巧highest index of an element that was explicitly assigned a value? other than to loop through and count? I know Ubound finds the highest dimension of the array but that's not what I need. Is there something like Ubound but it only counts populated elements of an array, or the highest index populated?

My array contains strings. Also, what if the array is multidimensional.

I'm working in excel vba.


In a short word, no there is no in built mechanism to do this, but if you are looking for a way to hack it, then read on.

It depends on how you are filling up the array. If it is sequential, the easiest would be having a counter.

If it is randomized then you have a bigger challenge of finding the exact index of the last element. One idea, is to start from the maximum and start counting down.

If the array is initialized, you can count the default values and subtract from the ubound.

There are other ideas, but you need to more clearly defined the problem to give us a better chance of answering. Preferably with an example.


I'd suggest using ReDim Preserve as you populate the array. Thus UBound will work quite nicely for you. Only the last dimension in a multi dimensional array can be ReDimmed.


Highest populated element of an array

You could step backwards through a loop until you find the first array element that isn't empty.
This is the equivalent of the "last" populated element.

Short answer:

Function HighestPopIdx(ByRef arr) As Long
    For HighestPopIdx = UBound(arr) To LBound(arr) Step -1
        If Not IsEmpty(arr(HighestPopIdx)) Then Exit Function
    Next
End Function

Longer answer:

To get the Highest populated element of an array, I would use a For..Next loop with the IsEmpty function.

(This example is a tad over-complicated, for the sake of demonstration.)

Sub demo() '(Hit CTRL+G in the VBE to view the output in the Immediate window.)

    'Make a test ARRAY of 5 elements:
    Dim arr(1 To 5)

    Erase arr '(just to make sure the test is starting 'fresh')

    arr(1) = "X"
    arr(2) = 99
    arr(4) = "Y" '3 of the 5 elements have data (highest one is 4)

    Dim elIdx As Long, highestPopEl As Long
    For elIdx = LBound(arr) To UBound(arr)
        If Not IsEmpty(arr(elIdx)) Then highestPopEl = elIdx
    Next elIdx

    Debug.Print "The highest populated element index is " & highestPopEl 'Returns 4

End Sub

...or in reverse (first example could be quicker depending on your situation.


Other notes:

Further to @jtolle's answer, neither Count nor CountA worksheet functions will work:

    With Application.WorksheetFunction
        Debug.Print "Array WS CountA", .CountA(arr) 'returns 5 (Counts cells)
        Debug.Print "Array WS Count", .Count(arr) 'returns 1 (Counts numbers)
    End With

...and further to the comments, showing that arrays & cell ranges function similarly but not the same:

    'Make a test RANGE of 5 cells
    Dim rge As Range
    Set rge = Range("A1:A5")

    rge.Clear '(maker sure we're starting fresh)

    Range("A1") = "X"
    Range("A2") = 99
    Range("A4") = "Y" '3 of the 5 cells have data

    With Application.WorksheetFunction        
        Debug.Print "Range WS CountA", .CountA(rge) 'returns 3 (Counts values)
        Debug.Print "Range WS Count", .Count(rge)   'returns 1 (Counts numbers)
    End With

   '... and the VBA [Range.Count] method behaves differently:
    Debug.Print "Range VBA .Count", rge.Count 'returns 5 (Counts cells)

On the other hand if my "actual" goal was to do something to each element in an array, I would use For..Each loop with the IsEmpty function.

    'if my actual purpose is to ***do something*** to an ARRAY...
    Debug.Print: Debug.Print "Array [For..Next] :"
    Dim elCnt As Long, el

    For Each el In arr
        If Not IsEmpty(el) Then _
            elCnt = elCnt + 1: Debug.Print "#" & elCnt & ": Element value =", el
    Next el    ' (returns "X", 99, "Y")

    '...and the identical method can be used on a RANGE of cells
    Debug.Print: Debug.Print "Range [For..Next] :"
    Dim clCnt As Long, cl

    For Each cl In rge
        If Not IsEmpty(cl) Then _
            clCnt = clCnt + 1: Debug.Print "#" & clCnt & ": Cell value =", cl
    Next cl    ' (returns "X", 99, "Y")


End Sub

Complete Output:

The highest populated element index is 4.

Array WS CountA              5 
Array WS Count               1 
Range WS CountA              3 
Range WS Count               1 
Range VBA .Count             5 

Array [For..Next] :
#1: Element value =         X
#2: Element value =          99 
#3: Element value =         Y

Range [For..Next] :
#1: Cell value =            X
#2: Cell value =             99 
#3: Cell value =            Y

More Info:

  • IsEmpty Function
  • UBound Function
  • For...Next Statement


You might consider using a custom class that contains an array variable, code to add items to the array which would also modify another variable to track the "used" index number. Then you could include a function to return the stored "highest index" number.


Note from 2018: This is wrong in Excel 365, and was probably wrong in Excel 2000. See the comments. But I'm leaving it up as an example of how using Application.WorksheetFunction() from VBA isn't always exactly doing the same thing as using a formula in the worksheet.


Of course, there's really nothing wrong with looping, but...

Say you have this:

Public Sub test()
    Dim a(1 To 3)

    a(1) = 1
    a(2) = "stuff"

    Debug.Print Application.WorksheetFunction.CountA(a)
End Sub

That will print '2' [*No, it prints 3 - See Comment***], since the third element has a default value of 'Empty'. (Note that your array has to be declared as type Variant for this to work, but it can contain elements of type String. If your array is of type String, there aren't any empties!) And of course, this assumes that you know all your empties are at the end, since it's just counting, not actually returning an index. For the same reason, you'd have to know the distribution of your values in a 2-D array for this to be meaningful.

I think this is what you're looking for, based on your edit.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜