reyman64
reyman64

Reputation: 553

bad number of element in dynamic array with for each loop

I don't understand why for each loop in vba doesn't return the good number of element when i use dynamic array.

For exemple, my array size is 4, and i have 5 iteration in for each loop ...

Public Sub test()

Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0

ReDim t_direction(4)

t_direction(0) = "N"
t_direction(1) = "S"
t_direction(2) = "E"
t_direction(3) = "W"

t_nextDirection = randomizeArray(t_direction)

For Each myDirection In t_nextDirection
    Var = Var + 1
Next myDirection

MsgBox (UBound(t_nextDirection))
MsgBox (Var)

End Sub

Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer

Dim tmp As String
Dim numItems As Integer

numItems = UBound(t_array) - 1

    ' Randomize the array.
    For i = 0 To numItems
        ' Pick a random entry.
        j = Rand(0, numItems)

        ' Swap the numbers.
        tmp = t_array(i)
        t_array(i) = t_array(j)
        t_array(j) = tmp
    Next i
    'MsgBox (UBound(t_array))
    randomizeArray = t_array

End Function

Public Function Rand(ByVal Low As Long, _
                     ByVal High As Long) As Integer
  Rand = Int((High - Low + 1) * Rnd) + Low
End Function

Upvotes: 1

Views: 272

Answers (2)

chris neilsen
chris neilsen

Reputation: 53135

ReDim t_direction(4) actually declares t_direction as 0 To 4

Its better to be explicit:

ReDim t_direction(0 To 3)

In the absence of a specified lower bound (using the To clause), then the default lower bound is used.
This default can be set to 0 or 1 by using Option Base {0|1} at module level.
In the absence of Option Base then the default default is 0

Notes:

In VBA you are not limited to 0 or 1 as the lower bound, you can use any value you want.

To iterate over an array use

For i = LBound(arr) To UBound(arr)

To calculate the number of items in an array use

numItems = UBound(arr) - LBound(arr) + 1

This way you are not making any assumptions on what the lower bound is

Upvotes: 2

brettdj
brettdj

Reputation: 55672

At the moment you are creating a 5 element array with
ReDim t_direction(4)
as the first element occurs as t_direction(0)

You should either

  • create a 4 element array ReDim t_direction(3) (ie 0 to 3) and then use numItems consistent with that, or
  • create a 4 element array ReDim t_direction with a base of 1 (ie 1 to 4) and then use numItems consistent with that (ie numItems = UBound(t_array)). The Option Base 1 below forces the first element to be 1 (which is then ensured anyow by using ReDim t_direction(1 To 4)

The code below uses the later approach. It returns 4 and 4 rather than your current 4 and 5

Option Base 1
Public Sub test()

Dim t_direction() As String
Dim t_nextDirection() As String
Dim myDirection As Variant
Dim test As Integer
Var = 0

ReDim t_direction(1 To 4)

t_direction(1) = "N"
t_direction(2) = "S"
t_direction(3) = "E"
t_direction(4) = "W"

t_nextDirection = randomizeArray(t_direction)

For Each myDirection In t_nextDirection
    Var = Var + 1
Next myDirection

MsgBox (UBound(t_nextDirection))
MsgBox (Var)

End Sub

Public Function randomizeArray(ByVal t_array As Variant) As String()
Dim i As Integer
Dim j As Integer

Dim tmp As String
Dim numItems As Integer

numItems = UBound(t_array)

    ' Randomize the array.
    For i = 1 To numItems
        ' Pick a random entry.
        j = Rand(1, numItems)

        ' Swap the numbers.
        tmp = t_array(i)
        t_array(i) = t_array(j)
        t_array(j) = tmp
    Next i
    'MsgBox (UBound(t_array))
    randomizeArray = t_array

End Function

Public Function Rand(ByVal Low As Long, _
                     ByVal High As Long) As Integer
  Rand = Int((High - Low + 1) * Rnd) + Low
End Function

Upvotes: 4

Related Questions