Variant Array and Arrays of Variant
Description
Although Array of Variants and Variant Array may sound like the same thing, they are two distinct things:
The first - Array of Variants - is a standard GFABASIC array dimensioned to hold Variant type variables and created thus...
Dim var(10) [As Variant]
...while the other is a Array of any or multiple variable types stored inside a Variant type variable (from now on referred to simply as Variant) created using the Array function like this...
Dim var = Array(1, 2, 3) [As Type]
This last type of array is also known as a SafeArray or a Dynamic Array.
The main differences between a GFA array of Variants and a Variant array are speed, flexibility, compatibility and usability within GFABASIC programs:
- Speed: There is no real competition in speed: using Variants in a GFABASIC array allows for much faster processing, as is shown if you run this code Show
// Initialise a GFABASIC and a Variant Array
Local a(10), b = Array(1, 2, 3, 4), n%, t1#, t2#
For n% = 0 To 10 : a(n%) = n% : Next n%
// Time the GFABASIC array...
t1# = Timer
For n% = 1 To 10000 : a(0) = a(1) + a(2) : Next n%
t1# = Timer - t1#
// ...and the Variant array...
t2# = Timer
For n% = 1 To 10000 : b(0) = b(1) + b(2) : Next n%
t2# = Timer - t2#
// ...then show the difference
Debug "GFABASIC Array of Variants:";t1#
Debug "Variant Array:";t2#
Debug "GFABASIC arrays are"; Round(((t2# / t1#) * 100), 2) ; "% faster."
Debug.Show
- Flexibility: Out of the two, Variant Arrays allow for greater flexibility in that you can embed one Variant Array inside another, inside another, inside another and so on; you can add an array to an element in a GFABASIC array of Variants but that array MUST be a Variant Array as it is not possible to assign a GFABASIC array of any type to a Variant. Otherwise, there is little difference between the two.
- Compatibility: Here too, Variant Arrays win out as they are used in other computing languages (VB, C++, etc.) and allow easier porting of code into GFABASIC; that said, standard array types similar to GFABASIC arrays are used in most BASIC type languages so the advantage for Variant Arrays is not as large as it might seem but, with the demise of BASIC-type languages, is growing greater by the year.
- Usability: Up until version 2.58, Variant Arrays had poor support within GFABASIC: you could not ReDim a Variant Array, use variables as array indexes nor change values in Variant Arrays embedded in other Variant Arrays as can be seen in the old Known Issues section for the Array function Show
Unlike VB and VB.NET, it does not support ReDim on a Variant containing an array. The only way to resize an array is to re-use the Array function as below:
Local v2 As Variant = Array(1, 15, "Last one")
Print v2(0), v2(1), v2(2)
v2 = Array(v2(0), v2(1), v2(2), "And now this one")
Print v2(0), v2(1), v2(2), v2(3)
It also only allows elements to be amended if the index is a constant not a variable:
Local v2 As Variant = Array(10, 12, 108, "Hello")
Local Const elem = 2 : Local Int32 elem% = 2
v2(2) = 20 : Print v2(2) // Prints 20
v2(elem) = 30 : Print v2(elem) // Prints 30
v2(elem%) = 40 : Print v2(elem%) // Throws an 'Object is Nothing' error
[Reported by James Gaite. 22/02/17]
Another problem with trying to set values in this type of array is found when the element is part of an array within another array. When typing in array(1)(2) = "Hello" for example, the error 'No compare string <=> number' is thrown; alternatively, if you try passing a numerical value auch as 5 to the same element, the editor rewrites the line as array(1), (2) = 5 and the message 'Error 0x80004003 Invalid pointer' is displayed.
The only way to get around this is to 'step' up to the desired array in which you want to make the change, alter it, then 'step' down again to reset the parent array, something like this:
Local v2 As Variant = Array(10, 12, 108, Array(5, "Hello"))
Local Variant a, b
a = v2(3)
a(0) = "Goodbye"
v2(3) = a
Print v2(3)(0)
[Reported by James Gaite. 22/02/17]
However, new commands and functions have been added as a Variants Library since version 2.58 which negate these issues and, although GFABASIC arrays of Variants still have the edge, it is much diminished.
Using GFABASIC Arrays of Variants is similar to using any other type of GFABASIC array - all the commands and functions work as normal - with the difference being in the vagaries of the Variant Type itself.
However, Variant Arrays are different, with some different keywords, and need further explanation.
Variant Arrays
There are only three GFABASIC keywords which work with Variant Arrays: Array, LBound and UBound. Added to these in version 2.58 onwards are VarArraySet and VarReDim, as well as VarLoad and VarSave which are universal Variant keywords.
Creating Variant Arrays
variant = Array([parameter list]) [As Type]
Show
Variant Arrays are created using the Array function and can be Re-Dim'ed (version 2.58 onwards) with the VarReDim command.
The Array function adds a dynamic array to a predefined Variant variable, adding values by means of a comma-delimited parameter list. The resulting array has the following characteristics:
- By default, all arrays created using this function are non-specific and of type Variant unless a type is specified using the optional As Type syntax. Hence, to create a ByteArray, you would use Array([values]) As Byte. The As syntax can only be used with a parameter list of which each member can legally be defined as the desired variable type.
- Integer and Boolean values added through the parameter list into an array are automatically converted to Int32/Long variables UNLESS the array is specifically defined as a different type (see 1 above) OR they are added through the medium of a predefined GFABASIC variable of the type you require, as show below:
Local a? , b| = 1, c& = 2
Local v = Array(a?, b|, c&, 3)
Debug TypeName(v(0)),TypeName(v(1)),TypeName(v(2)),TypeName(v(3)) // Boolean Byte Short Integer
- In a similar vein to (2), all floating point numbers are automatically converted to Double UNLESS the array is specifically defined as a different type (see 1 above) OR they are added using the CSng or CCur functions OR they are added through the medium of a predefined GFABASIC variable of the type you require.
- To add an object, you first need to add it to a Variant and then include that Variant in the parameter list.
- To embed an array inside the array to be created, you can either insert an Array() statement into the parameter list or assign the array to a different Variant and include that Variant in the parameter list, like in the example below:
Local b = Array("Hello", "Goodbye") As String
Local v = Array(1, 3.4, b, Array(1, 4, 88) As Byte)
- Dates and Strings are automatically converted to their respective variable types when entered as literals or GFABASIC variables of the required type.
- The Null, Missing and Empty values can be added using the constants Null, Missing and Empty.
- Error values can be added using the CVErr function (note: this requires the Variants.lg32 library).
- The lower bound of an array created using the Array function is always 0, regardless of the value of Option Base; the bounds of the array can be redefined later by using VarReDim (see below).
Re-sizing Variant Arrays
Requires: Variants.lg32
VarReDim variant, elements%
VarReDim variant, "LBound TO UBound"
elem% = VarSize(variant array)
Show
VarReDim comes in two forms: the first where the new number of elements is specified with the lower bound remaing the same; and the second where the range of elements from the lower to the upper array bound is passed as a string. The following examples shows how each form can be used:
$Library "Variants.lg32"
Local v = Array(0, 1, 2, 3, 4, 5) As Byte // Creates a Byte array, bounds 0 to 5
Debug VarPrint(v, VARPRINT_ARRAYINFULL)
VarReDim v, 4 // ReDims to 4 elements, bounds 0 to 3
Debug VarPrint(v, VARPRINT_ARRAYINFULL)
VarReDim v, "2 TO 7" // ReDims again to 6 elements, bounds 2 to 7
Debug VarPrint(v, VARPRINT_ARRAYINFULL)
VarReDim v, 4 // ReDims a last time to 4 elements, bounds 2 to 5
Debug VarPrint(v, VARPRINT_ARRAYINFULL)
Debug.Show
Due to the way that Variant parameters are passed in GFABASIC (Variant Arrays ByRef, elements of Variant Arrays ByVal), only the parent array can be ReDim'ed using this command; to ReDim an embedded array, you need to do something like this:
$Library "Variants.lg32"
Dim a = Array(1, 2.2, Array("Dog", "Cat", 144.56), "last")
Debug VarPrint(a, VARPRINT_ARRAYINFULL)
Local b = a(2) // Assign the child array to a new variant
VarReDim b, "1 to 5" // Use the new variant to ReDim the array
VarArraySet a, 2, b // Set the new variant in place of the old child array
Debug VarPrint(a, VARPRINT_ARRAYINFULL)
Debug.Show
VarSize (requires version 3 of the Variant Library) returns the number of elements in a particular index of an array - if any of those elements are themselves an array, it does not iterate through this sub-array but simply counts it as one element.
$Library "Variants.lg32"
Local a = Array(12, 12, Array(15, "a"), 4)
Trace VarSize(a) // Returns 4 (counts array in a(2) as one element)
Trace VarSize(a(2)) // Returns 2
Trace VarSize(a(1)) // Raises an error as a(1) is not an array
Reading and Setting Elements in Variant Arrays
Requires: Variants.lg32
VarArraySet variant, index1[, index2, ....], value
Show
The syntax for accessing Variant Arrays is the same as that used for GFABASIC arrays for single index arrays, but differs when referring to an array inside an array. To read the element zero of an array, you would use array(0), to read element 3 of an array embedded in element 2 of the parent array, you would use array(2)(3) as in the example below:
Local v = Array(1, 3.4, Array(1, 4, 88, 122) As Byte)
Debug v(0) // 1
Debug v(2)(3) // 122
The problem in GFABASIC comes when you try and set the elements in an array. To set elements in a single index array, you can use the format array(n) where n is either a literal or a constant - if it is a variable, then GFABASIC throws an error.
Therefore, from Version 2.58 onwards, you should use the VarArraySet command to set element values. The first parameter is the actual variant containg the array, the second the first (and maybe only) index element number, the third the second index element number (if required) and so on, and then, finally, the last parameter is the value you wish to set. This is illustrated in the following example:
$Library "Variants.lg32"
Local v = Array(1, 3.4, Array(2, Array(1, 4, 88, 122) As Byte))
Debug v(2)(1)(3) // 122
VarArraySet v, 2, 1, 3, 23 // Set the element containg 122 to 23
Debug v(2)(1)(3) // 23
Debug.Show
Due to the way that Variant parameters are passed in GFABASIC (Variant Arrays ByRef, elements of Variant Arrays ByVal), only the parent array can be used with this command; using a child or embedded array will result in an error being thrown.
Inserting and Deleting Elements in Variant Arrays
Requires: Variants.lg32 version 3
VarInsert variant, elemno%, value [, options%]
VarDelete variant, elemno% [, options%]
Show
These commands duplicate - and expand upon - the functions of the GB32 array commands Insert and Delete.
By default, VarInsert inserts value into element number elemno%, shifting all other elements one down with the last element being lost, just like Insert; by adding the flag VARINSERT_INCREASE to options%, the array is increased by one element and, therefore, the last element is no longer lost; and by adding VARINSERT_INSERTAFTER, value is added after the element number elemno% and not before.
Similarly, VarDelete acts like Delete by default, removing the value stored in element elemno%, shifting all later elements up one and inserting an Empty value into the last element; however, with VarDelete, by adding VARDELETE_REDUCE to options%, the element is deleted, the later elements shifted one up but the array is then reduced by one element.
$Library "Variants.lg32"
Local a = Array(10, Array(2, 5) As Byte, 5, 10)
Local b, c = Array("Hello", 24), d, ie As Object
Set ie = CreateObject("InternetExplorer.Application")
Trace VarPrint(a, VARPRINT_ARRAYINFULL) // Show the structure of the array
b = a(1) // Move sub array to b...
VarDelete b, 0 // ...delete element 0, but keep the array the same length...
VarArraySet a, 1, b // ...then replace the sub array into the main array
VarInsert a, 1, c, VARINSERT_INCREASE // Insert sub array c into element 3 of the array and increase the array by one to accomodate
Trace VarPrint(a, VARPRINT_ARRAYINFULL) // Show the structure of the amended array
d = a(1) // Move new sub array to b...
VarInsert d, 1, ie, VARINSERT_INCREASE | VARINSERT_INSERTAFTER // ...insert an IE object AFTER element one and increase the sub-array to accomodate...
VarDelete d, 1, VARDELETE_REDUCE // ...then remove the value in element one and reduce the size of the sub-array by one...
VarArraySet a, 1, d // ...then replace the sub array into the main array
Trace VarPrint(a, VARPRINT_ARRAYINFULL) // Show the structure of the amended array
Set ie = Nothing
Other useful keywords are:
- VarLoad and VarSave which can be used to save Variant arrays to file;
- VarPrint which can be used to print the contents of a Variant Array as a data tree for debugging purposes;
- IsArray which can be used to determine whether a Variant or element of a Variant Array contains an array;
- and LBound and UBound which can be used to return the lower and upper bounds of parent and embedded arrays.
See Also
Variant Type.
{Created by James Gaite; Last updated: 26/11/2023 by James Gaite; Other Contributors: Jean-Marie Melanson}