1

I want to use a dynamic array containing arbitrary number of strings. The array is filled by if ... then logic instead of a loop. I keep getting Subscript out of range error:

Dim Files() As String

If True Then
    ReDim Preserve Files(UBound(Files) + 1) ' Throws "Subscript out of range" error
    Files(UBound(Files)) = "foo.pdf"
End If

If True Then
    ReDim Preserve Files(UBound(Files) + 1)
    Files(UBound(Files)) = "bar.txt"
End If

If True Then
    ReDim Preserve Files(UBound(Files) + 1)
    Files(UBound(Files)) = "baz.jpg"
End If

I have a function declared like this:

Function SendFiles(Files() As String)

I want to get rid of this error without using variants if possible. I can rewrite the code but I cannot use a loop.

Cindy Meister
  • 25,071
  • 21
  • 34
  • 43
Salman A
  • 262,204
  • 82
  • 430
  • 521
  • 3
    An unsized array has no `UBound`. – BigBen Nov 08 '19 at 13:25
  • @bigben what would be the cleanest approach to handle this? I don't know in advance how many entries need to be added, there is no loop, and I am asked to use types wherever possible. – Salman A Nov 08 '19 at 13:26
  • 2
    You could just use `ReDim Files(0)` before your first `If`. If you know the largest possible size at the outset you can redim to that at the outset and then cut out any unnecessary elements at the end using Preserve. – SJR Nov 08 '19 at 13:29
  • 3
    You know `ReDim Preserve` allocates an entire new array, copies elements one by one, and then updates the original reference, right? It's a pig for performance. If you have an array you expect to append to often, you should look at the [`Collection`](https://learn.microsoft.com/en-us/office/vba/language/reference/user-interface-help/collection-object) type. – Joel Coehoorn Nov 08 '19 at 14:31
  • `If True Then` are these lines needed? When will TRUE not be TRUE? – Darren Bartrup-Cook Nov 08 '19 at 15:06

2 Answers2

2

Your array is not initialized at the start, and you can't Redim Preserve an uninitialzed array.

If you want a string array to hold a variable amount of items, possibly zero, you can start with initializing it to a zero-length array using Split:

Files = Split(vbNullString)
Erik A
  • 31,639
  • 12
  • 42
  • 67
  • This seems to do the job. Is there a language construct to initialize an array declared as `Dim Files() As String` an array having UBound = -1? – Salman A Nov 08 '19 at 18:20
  • @SalmanA No, unfortunately, there's not. You can either use `Split(vbNullString)` to get a 0-length string array, or a complicated WinAPI call to do it with a little less overhead. See [This Q&A](https://stackoverflow.com/q/55123413/7296893) for more background, getting zero-length array is difficult for anything but strings. – Erik A Nov 08 '19 at 18:36
1

You could also allocate a large enough array and resize to used size afterwards. That way you have only 1 resize. Something like this:

Dim Files(1000) As String, i as long

If True Then
    Files(i) = "foo.pdf": i = i+1
End If

If True Then
    Files(i) = "bar.txt": i = i+1
End If

If True Then
    Files(i) = "baz.jpg": i = i+1
End If

redim preserve Files(i-1)
iDevlop
  • 24,841
  • 11
  • 90
  • 149