Macros Explained - Array Routines
Macros Explained - Array Routines
Array Routines
This chapter introduces the subroutines and functions supported by OOo Basic that are used to manipulate
arrays. It covers methods for manipulating arrays, creating arrays with data, creating arrays with no data, and
changing the dimension of arrays. This chapter also presents methods to inspect array variables.
An array is a data structure in which similar elements of data are arranged in an indexed structure — for
example, a column of names or a table of numbers. OOo Basic has subroutines and functions that change
array dimensions, inspect existing arrays, and convert between arrays and scalar (non-array) data types.
The majority of the routines listed in Table 32 require an array variable as the first argument. Array variables
used as arguments to routines can be written with trailing parentheses. Parentheses after the variable are
optional, but they used to be required (see Listing 92).
TIP There is no way to determine if a() refers to an array or a function while reading code; you must find where
the item in question is declared.
Listing 92. Parentheses are not always required but are always allowed.
Sub AreArrayParensRequired
Dim a(1 To 2) 'a() is declared with specified dimensions
Dim b() 'b() is declared as an array without specified dimensions
Dim c 'c is a variant and may reference an array.
c = Array(1, 2) 'c references a Variant array
Print IsArray(a()) 'True
Print IsArray(b()) 'True
Print IsArray(c()) 'True
Print IsArray(a) 'True
Print IsArray(b) 'True
Print IsArray(c) 'True
End Sub
LBound(array) Return the lower bound of the array argument. The optional dimension specifies
LBound(array, dimension) which dimension to check. The first dimension is 1.
ReDim [Preserve] var(args) [As Type] Change the dimension of an array using the same syntax as the DIM statement.
The keyword Preserve keeps existing data intact. “As Type” is optional.
Split(str) Split the string argument into an array of strings. The default delimiter is a space.
Split(str, delimiter) The optional argument n limits the number of strings returned.
Split(str, delimiter, n)
UBound(array) Return the upper bound of the array argument. The optional dimension specifies
UBound(array, dimension) which dimension to check. The first dimension is 1.
113
The word “dimension” is used to refer to arrays similarly to the way that “dimensions” is used to refer to
spatial dimensions. For example, an array with one dimension is like a line; you can set boxes along the line
that represent data. An array with two dimensions is like a grid with rows and columns of data.
Dim a(3) As Integer 'One-dimensional array
Dim b(3 To 5) As String 'One-dimensional array
Dim c(5, 4) As Integer 'Two-dimensional array
Dim d(1 To 5, 4) As Integer 'Two-dimensional array
This can be done in a much simpler manner. Constants can be used as arguments to functions. You aren’t
always forced to assign a value to a variable so that you can call a function.
Dim v()
Dim FirstName$ : FirstName = "Bob"
v() = Array(0, "help", Now, True, 3.5)
Print Join(Array("help", 3, "Joe", Firstname))
The argument list is a comma-separated list of expressions, which can be of any type because each element
in the returned array is a variant.
Listing 93. The Array function returns a Variant array.
Dim vFirstNames 'A Variant can reference an array
Dim vAges() 'A Variant array can reference an array
vFirstNames = Array("Tom", "Rob") 'Array contains strings
vAges = Array(18, "Ten") 'Array contains a number and a string
Print vAges(0) 'First element has the value 18
Variant variables can contain any type, including an array. I frequently use variant variables when I retrieve
values from methods when I’m not certain of the return type. I then inspect the type and use it appropriately.
This is a convenient use of the Variant variable type. Because a Variant variable can contain any type —
including an array — each element in a Variant array can also contain an array. The code in Table 33
demonstrates placing an array inside another array. The code in each column is roughly equivalent.
TIP Although user defined structures cannot contain arrays, they can contain a variant, which can contain an
array.
One advantage of Variant arrays is that it’s possible to easily build collections of information of different
types. For example, the item description (String), stock-tracking ID (Integer), and invoice amount (Double
or Currency) can readily be stored as rows in an array, with each type of data stored in a single row for each
customer. This allows array rows to behave more like rows in a database. Older programming languages
required use of separately declared arrays for each data type, with added programming overhead of
managing the use of multiple, related arrays of data.
114
Table 33. A Variant can contain an array, so these accomplish the same thing.
Dim v(1) Dim v()
v(0) = Array(1, 2, 3) v = Array(Array(1, 2, 3),_
v(1) = Array("one", "two", "three") Array("one", "two", "three"))
In OOo version 1.x, to address an array containing an array, you had to first extract the contained array, and
then subscript the contained array. Much of my existing code was written based on this.
Listing 94. Cumbersome method to subscript an array in an array.
v = Array(Array(1, 2, 3), Array("one", "two", "three"))
x = v(0) 'This is very cumbersome.
Print x(1) 'Prints 2
Somewhere between version 2.x and 3.x, the obvious solution was introduced; you can directly access the
contained array. This is particularly useful while using data arrays returned by Calc containing cell data.
Listing 95. In OOo 3.x, you no longer need to extract the contained array to use it.
Sub ArrayInArray
Dim v() : v = Array(Array(1, 2, 3), Array("one", "two", "three"))
Print v(0)(1)
End Sub
Although it’s easy to create an array inside of an array, it’s typically easier to use an array with multiple
dimensions, as shown in Listing 96. The “array of arrays” construction is sometimes useful, if there is an
obvious relationship with the natural organization of the data. Generally, it is best to select a way to organize
the data that has the most direct, natural, and memorable relationship to how it is produced, used, and
manipulated.
Listing 96. It is easier to use multi-dimensioned arrays than arrays inside of arrays.
Dim v(0 To 1, 0 To 2)
v(0, 0) = 1 : v(0, 1) = 2 : v(0, 2) = 3
v(1, 0) = "one" : v(1, 1) = "two" : v(1, 2) = "three"
Print v(0, 1) 'prints 2
A Variant array can be assigned to any other array, regardless of its declared type. Assigning one array to
another causes one array to reference the other; they become the same array. As mentioned earlier, this is a
bad idea. This is considered a bug and it may not be allowed in later versions of OOo Basic. Use the Array
function and enjoy the flexibility, but assign the returned Variant array to either a Variant or a Variant array.
Listing 97. Assign an string array to an integer array.
Sub BadArrayTypes
Dim a(0 To 1) As Integer
Dim b(0 To 1) As String
b(0) = "zero": b(1) = "one"
a() = b()
Print a(0)
End Sub
115
TIP Assigning a Variant array to variables declared as a non-Variant array is ill-advised. For example, after an
Integer array has been assigned to reference a Variant array, it’s possible to assign non-integer values to
elements in the array. References to the array won’t return the expected values due to the mismatch
between Integer and Variant data types.
Dim a(0 To 4) As Integer ' Create an array of Integers.
a(2) = "Tom" ' Assign a string to an Integer variable.
Print a(2) ' 0, because the string is converted to zero.
a() = Array(4, "Bob", 7) ' Array always returns a variant array.
a(2) = "Tom" ' a() is now a variant.
Print a(2) ' Tom
The code in Listing 98 does not show how the variable v is declared. This works equally well if v is declared
as a Variant, or a Variant array. The argument list is a comma-separated list of expressions. Each expression
is rounded to an integer and used to set the range of one dimension of the returned array.
Dim a As Variant
Dim v()
Dim i As Integer
i = 2
a = DimArray(3) 'Same as Dim a(0 To 3)
a = DimArray(1+i, 2*i) 'Same as Dim a(0 To 3, 0 To 4)
TIP Option Base 1 has no effect on the dimensions of the array returned by the DimArray function. For each
dimension, the lower bound of the range is always zero and the upper bound is the rounded integer value of
the relevant expression.
116
5.3. Change the dimension of an array
Use ReDim to change the dimensions of an existing array by using the same syntax as the DIM statement.
Increasing the dimension of an array while using the keyword Preserve preserves all of the data, but
decreasing the dimension causes data to be lost by truncation. Unlike some variants of BASIC, OOo Basic
allows all dimensions of an array to be changed while also preserving data.
The primary use of the ReDim statement is to change the dimension of an existing array. If you know the
size of the array that you will need, you can declare it when you declare the variable. If you don’t know the
size ahead of time, you can declare the array with any size, including as an empty array, and then change the
dimension when you know it.
Dim v() As Integer
Dim x(4) As Integer
i% = 7
ReDim v(3*i%) As Integer 'Same as Dim v(0 To 21) As Integer.
ReDim x(i%, 1 To 4) As Integer 'Same as Dim x(0 To 7, 1 To 4).
The ReDim statement changes the dimension of an existing array, even an empty one. ReDim specifies both
the dimensions and the type. The type specified with the ReDim statement must match the type specified
when the variable is declared. If the types differ, you’ll see the compile-time error “Variable already
defined.”
Dim a() As Integer 'Empty Integer Array.
Dim v(8) 'Variant array with nine entries.
ReDim v() 'v() is a valid empty array.
ReDim a(2 To 4, 5) As Integer 'a() is a two-dimensional array.
The DimArray function creates and returns a dimensioned Variant array that contains no data. This is not
useful if you require an array of a specific type, or if you simply need to change the dimensions of an
existing array while preserving data. The ReDim statement changes the dimensions of an existing array with
the option of preserving existing data. You can use the ReDim statement to change a dimensioned array to an
empty array.
The subroutine in Listing 99 contains many examples of the ReDim statement using the keyword Preserve.
Figure 43 shows the results of these commands.
Listing 99. Use ReDim with Preserve to change dimension and preserve data.
Sub ExampleReDimPreserve
Dim a(5) As Integer 'A dimensioned array, 0 To 5
Dim b() 'An empty array of type Variant
Dim c() As Integer 'An empty array of type Integer
Dim s$ 'The string that accumulates the output text
117
REM Array() returns a Variant type
Rem b is dimensioned from 0 to 9 where b(i) = i+1
b = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
s$ = s & CHR$(10) & "b() at start = " & Join(b()) & CHR$(10)
ReDim b(-5 To 5)
s$ = s$ & "ReDim b(-5 To 5) = " & Join(b()) & CHR$(10)
s$ = s$ & "ReDim b(-5 To 5) has LBound = " &_
LBound(b()) & " UBound = " & UBound(b()) & CHR$(10) & CHR$(10)
ReDim b(-5 To 5, 2 To 4)
s$ = s$ & "ReDim b(-5 To 5, 2 To 4) has dimension 1 LBound = " &_
LBound(b()) & " UBound = " & UBound(b()) & CHR$(10)
s$ = s$ & "ReDim b(-5 To 5, 2 To 4) has dimension 2 LBound = " &_
LBound(b(), 2) & " UBound = " & UBound(b(), 2) & CHR$(10)
118
The Split function returns a Variant array of strings, created by breaking a string into multiple strings based
on a delimiter. In other words, it parses a string into pieces with one command. The delimiter separates
portions of the string. For example, the delimiter “XY” splits “12XY11XY22” into the strings (“12”, “11”,
“22”). The delimiter defaults to a space but can be any string expression with length greater than zero.
Split("1 2 3") 'return Array("1", "2", "3") split on " "
Split("1, 2, 3", ", ") 'return Array("1", "2", "3") split on ", "
The optional third argument is used to limit the size of the returned array. This is used only to limit the
returned size and has no effect if the returned size is less than the limit. For example, the 4 in
Split(“1X2X3”, “X”, 4) has no effect because the returned array has only three elements. If the size is
limited, however, the last element in the array contains the remainder of the unparsed string.
Split("1, 2, 3", ", ", 2) 'return Array("1", "2, 3") split on ", "
TIP The second argument to Split is a string, so, OOo automatically converts it to a string. The statement
Split(“0 1 2 3”, 2) converts the 2 to a string and uses it as the delimiter. The returned array contains two
elements, “0 1 ” and “ 3”. You must specify the delimiter if you want to specify the number of strings
returned. The correct format is Split(“0 1 2 3”, “ ”, 2).
The Split function assumes that a string comes before and after each delimiter, even if the string has length
zero.
Split("X1XX2X", "X") = ("", "1", "", "2", "")
The first returned string is empty because the first argument contains a leading delimiter. Two consecutive
delimiters produce an empty string between the “1” and the the “2”. Finally, the trailing string is empty
because there is a trailing delimiter.
The Split function is almost the inverse of the Join function. The Join function can use a zero-length string
as the delimiter, but the Split function cannot. If the joined string contains the delimiter, splitting the string
will produce a different set of strings. For example, joining “a b” and “c” with a space produces “a b c”.
Splitting this with a space produces (“a”, “b”, “c”), which is not the original set of strings.
I spent a lot of time writing and debugging a macro to parse through a string to remove all occurrences of the
text “Sbx”. Using Split and Join is significantly smaller and faster:
Join(Split(s, "Sbx"), "")
119
Print UBound(b(),2) ' 5
If the value of the second argument doesn’t contain a valid value, if it’s greater than the number of
dimensions, or if it’s less than 1, a run-time error occurs.
Listing 100. SafeUBound will not generate an error.
Function SafeUBound(v, Optional n) As Integer
SafeUBound = -1 'If an error occurs, this is already set
On Error GoTo BadArrayFound 'On error skip to the end
If IsMissing(n) Then 'Was the optional argument used?
SafeUBound = UBound(v)
Else
SafeUBound = UBound(v, n) 'Optional argument is present
End If
BadArrayFound: 'Jump here on error
On Error GoTo 0 'Turn off this error handler
End Function
The macro in Listing 100 properly returns -1 if an error occurs. The proper value is returned for invalid
empty arrays, but it also returns -1 if the first argument isn’t an array or if the second argument is simply too
large. The ArrayInfo function in Listing 101 uses a similar technique to return array information about a
variable. Also see Figure 44.
Listing 101. Print information about an array.
REM If the first argument is an array, the dimensions are determined.
REM Special care is given to an empty array that was created using DimArray
REM or Array.
REM a : Variable to check
REM sName : Name of the variable for a better looking string
Function arrayInfo(a, sName$) As String
120
REM Initial pretty return string
s = "Array dimensioned as " & sName$ & "("
If iCurDim > 1 Then s = s & ", " 'Separate dimensions with a comma
s = s & i & " To " & j 'Add in the current dimensions
iCurDim = iCurDim + 1 'Check the next dimension
Loop
BadDimension:
REM Turn off the error handler
On Error GoTo 0
Sub UseArrayInfo
Dim i As Integer, v
Dim ia(1 To 3) As Integer
Dim sa() As Single
Dim m(3, 4, -4 To -1)
Dim s As String
s = s & arrayInfo(i, "i") & CHR$(10) 'Not an array
s = s & arrayInfo(v, "v") & CHR$(10) 'Empty variant
s = s & arrayInfo(sa(), "sa") & CHR$(10) 'Empty array
s = s & arrayInfo(Array(), "Array") & CHR$(10) 'BAD empty array
s = s & arrayInfo(ia(), "ia") & CHR$(10)
s = s & arrayInfo(m(), "m") & CHR$(10)
MsgBox s, 0, "Array Info"
121
End Sub
Figure 44. Use proper error handling to determine the dimension of the array.
An array with one dimension may have an upper bound that is less than the lower bound. This indicates that
the array has no allocated spots for data. This is different than an array that has data locations allocated but
no data has been saved in them. For most data types, such as Integer, if space is allocated for an integer, then
it has a value.
Dim a(3) As Integer 'This array has four integer values, they are all zero
Dim b(3) 'This array has four Variants, they are all Empty
Dim c() 'This array has one dimension and no space Ubound < Lbound
v = Array() 'This array has zero dimensions.
5.6. Conclusion
Array handling in OOo Basic is very flexible. You have the ability to inspect arrays and to change their
dimensions. Using the Variant type in OOo Basic provides a great deal of flexibility for creating collections
of related data of different types. Strings and arrays are related; string arrays can be processed with Join and
Split functions, permitting the creation of compact code that is very powerful for processing string
information.
122