Can an array be declared as a constant? - arrays

Is it possible to either:
Declare an array as a constant
OR
Use a workaround to declare an array that is protected from adding, deleting or changing elements, and therefore functionally constant during the life of a macro?
Of course I could do this:
Const myConstant1 As Integer = 2
Const myConstant2 As Integer = 13
Const myConstant3 As Integer = 17
Const myConstant4 ...and so on
...but it loses the elegance of working with arrays. I could also load the constants into an array, and reload them each time I use them, but any failure to reload the array with those constant values before use could expose the code to a "constant" value that has changed.
Any workable answer is welcome but the ideal answer is one that can be setup once and not require any changes/maintenance when other code is modified.

You could use a function to return the array and use the function as an array.
Function ContantArray()
ContantArray = Array(2, 13, 17)
End Function

How about making it a function? Such as:
Public Function myConstant(ByVal idx As Integer) As Integer
myConstant = Array(2, 13, 17, 23)(idx - 1)
End Function
Sub Test()
Debug.Print myConstant(1)
Debug.Print myConstant(2)
Debug.Print myConstant(3)
Debug.Print myConstant(4)
End Sub
Nobody can change it, resize it, or edit its content... Moreover, you can define your constants on just one line!

I declared a String constant of "1,2,3,4,5" and then used Split to create a new array, like so:
Public Const myArray = "1,2,3,4,5"
Public Sub createArray()
Dim i As Integer
A = Split(myArray, ",")
For i = LBound(A) To UBound(A)
Debug.Print A(i)
Next i
End Sub
When I tried to use ReDim or ReDim Preserve on A it did not let me. The downfall of this method is that you can still edit the values of the array, even if you can't change the size.

If the specific VBA environment is Excel-VBA then a nice syntax is available from the Excel Application's Evaluate method which can be shortened to just square brackets.
Look at this
Sub XlSerialization1()
Dim v
v = [{1,2;"foo",4.5}]
Debug.Assert v(1, 1) = 1
Debug.Assert v(1, 2) = 2
Debug.Assert v(2, 1) = "foo"
Debug.Assert v(2, 2) = 4.5
'* write all cells in one line
Sheet1.Cells(1, 1).Resize(2, 2).Value2 = v
End Sub

If you don't need a new instance each time you can use a Static local variable to avoid multiple objects creation and initialization:
Private Function MyConstants()
Static constants As Variant
If IsEmpty(constants) Then
constants = Array(2, 13, 17)
End If
MyConstants = constants
End Function

Can an array be declared as a constant? No.
Workarounds - Simplest one I can think of is to define a constant with delim and then use Split function to create an array.
Const myConstant = "2,13,17"
Sub Test()
i = Split(myConstant, ",")
For j = LBound(i) To UBound(i)
Debug.Print i(j)
Next
End Sub

Is this too simplistic?
PUBLIC CONST MyArray = "1,2,3,4"
then later in a module:
Dim Arr as Variant
SET Arr = split(MyArray,",")

I know this is an old question, but these archives are often scanned for many years after being posted, so I don't see a problem with adding things long after the origin date.
How about creating a class, with a read-only property returning the 'array' value? You can specify a parameter using the same syntax as an array index, and defining only a GET property effectively makes it read-only. Define the constant values inside the class and it will work just like a constant array, even though the actual construction is different.

No - arrays can't be declared as constant but you can use a workaround.
You can create a function that returns the array you want
http://www.vbaexpress.com/forum/showthread.php?1233-Solved-Declare-a-Constant-Array

Using above information, I came to following working solution for comparing short text information of a month, independent from Excel using German language:
Const MONATE = ",Jän,Feb,März,Apr,Mai,Jun,Jul,Aug,Sep,Okt,Nov,Dez"
.. and later in the code:
If StringToCompare = Split(MONATE, ",")(Month(dt)) Then
NOTE: as the Split-Array starts with index 0 I added the comma in the beginning.

Don't know when this changed, but in Excel 365, this works (or, at least, does not generate a compiler error):
Const table1Defs As Variant = Array("value 1", 42, Range("A1:D20"))

Related

OpenOffice basic: declaring an array constant

I know this must be stupid, but I really did a lot of search on this site, apache.org and googled a lot, but I still ain't able to do something like
const indRangeNumbers(5) = {7,7,12,6,8}
Error: syntax
global indRangeNumbers (5) = Array {7,7,12,6,8}
Error: , expected
or even
global indRangeNumbers() as variant
sub assignArrays()
indRangeNumbers = Array {7,7,12,6,8}
end sub
Assign to a Variant variable which will contain a single Array object, so no need for ().
Global indRangeNumbers As Variant
Sub assignArrays()
indRangeNumbers = Array(7,7,12,6,8)
End Sub
An example is at https://forum.openoffice.org/en/forum/viewtopic.php?f=20&t=46161. Also see the DevGuide for how to initialize arrays at https://wiki.openoffice.org/wiki/Documentation/DevGuide/ProUNO/Basic/Mapping_of_Sequences_and_Arrays.

Type conversion (typecasting). Convert array to matrix (C code to Visual Basic)

I am working at rewriting C code to Visual Basic now. And I got some problems with this syntaxes:
C code:
typedef uint8_t state_t[4][4];
static state_t* state;
void myFunc (uint8_t* output)
{
state = (state_t*)output;
// some actions
}
My Visual Basic code:
Dim state(3, 3) As Byte
Sub myVBFunc(ByRef output() As Byte)
state = output ' does not work: Cannot convert Byte() to Byte(*,*)'
'state = CType(output, Byte(,)) ''also does not work'
' some actions'
End Sub
I can do it in C, because array name is a pointer to the first element. And C can automatically converts this types. So VB cannot. I'm stacked here.
How to convert this array to matrix?
Or how to avoid using of this in VB?
P.S. Actually I work in Visual Basic 6.0. (try to modify old project).
If the question is about VB6, then you have three options:
a. Change everything to use dynamic arrays:
Dim state() As Byte
Sub myVBFunc(ByRef output() As Byte)
state = output
End Sub
You can set up the array to call myVBFunc by redimensioning e.g.:
Dim myarray() as Byte
ReDim myarray(3,3)
myVBFunc(myarray)
b. Embed the fixed size array in a user defined type and pass that around instead:
Type type_state
state(3, 3) As Byte
End Type
Dim state As type_state
Sub test()
Dim something As type_state
myVBFunc something
End Sub
Sub myVBFunc(ByRef output As type_state)
state = output
End Sub
c. Use objects. Set up a class to hold your fixed sized array and set up getters and setters to access it. A short answer would be to long for me to show in this post.
(You will need extra code to set up the array itself)
I used simple way: just assign it trough the loop using index multiplication.
For k = 0 To 3
For j = 0 To 3
state(k, j) = temp(4 * k + j)
Next j
Next k
Why I didn't use Guillermo Phillips's answer:
a. I can't replace state(3,3) from my question just as
Dim state() As Byte
because there is some specific math here which is really simple for matrices.
I found useful ReDim option. Especially when use it in this way:
ReDim Preserve input(15)
It saves previous values of input array or put zeros in new positions!
But we can convert array to matrix using ReDim:
ReDim cannot change the number of dimensions
b. Idea of using of user defined type is really interesting but I stall can't figure out how to assign then Byte() to my
Type type_state
state(3, 3) As Byte
End Type
c. Agree! This way is useful. I just avoid it because of it's complicity (much easier to create function with inbuilt loop). But maybe using object is more safety?..

Definitions of property procedures for the same property are inconsistent [using arrays]

I am having trouble with this pesky error message (see title)
I have seen it on a lot of posts on this site and others and it is usually some dumb mistake and try as I might I cannot see what dumb thing I am doing.
Public Property Get Contents() As Variant
Contents() = pContents()
End Property
Public Property Let Contents(Values() As Variant)
pContents = Values()
End Property
Public Property Get Content(Index As Integer) As String
Content = pContents(Index)
End Property
Public Property Let Content(Index As Integer, Value As String)
...
The aim being that the pair of get, let two allow the entire array to be read/written and the second allows read/writing to individual elements of the array by index.
According to this: https://msdn.microsoft.com/en-us/library/office/gg251357.aspx
Let and Get property statement thingies (Yes, I am fairly new to this) for a class in a class module have to meet certain requirements (which I am curious as to why exactly?). As far as I can tell, mine meet those requirements:
My Let has one more parameter (2) than my Get (1) - I'm assuming here that the "as string" outside of the brackets doesn't count as an argument.
Also I use the same types (integer and string) in both the Let and Get, I have no need for a Set so that shouldn't be a problem. I have also tried changing the names of the parameter variables to no avail.
So please help me, what is wrong and how can I avoid this mistake in the future?
Well I seem to have resolved the issue myself, posting the answer here in case it helps others! :)
Precautions to make when setting class properties to arrays (and reading them)
1) be careful with brackets(): don't use them in the Let statement
Public Property Let ArrayProperty (newArray as variant)
as opposed to the tempting
Public Property Let ArrayProperty (newArray() as variant)
Similarly, don't use them when using this property e.g:
Class1.ArrayProperty = myArray
no brackets after myArray
2) Check that your arrays are not empty in property (myArray) Let statements
Link here VBA: Don't go into loop when array is empty to the answer that helped me accomplish this, it seems you have to build your own function or use error handling to do it,
IsEmpty(myArray)
won't work.
and the code snippet adapted for my purposes (credit to original snippet goes to CreamyEgg):
Public Function IsEmptyArray(TestArray) As Boolean
Dim lngUboundTest As Long
lngUboundTest = -1
On Error Resume Next
lngUboundTest = UBound(TestArray)
On Error GoTo 0
If lngUboundTest >= 0 Then
IsEmptyArray = False
Else
IsEmptyArray = True
End If
End Function
3) remember that you might need to redim your property array to the Ubound(newArray), e.g.
Public Property Let Contents (newArray as variant)
'redim pArray to size of proposed array
ReDim pArray(1 To UBound(newArray))
'above line will throw an exception if newArray is empty
pArray = newArray
end Property
4) to deliberately make a class property an empty array, I used a temporary array variable, publicly declared outside of a sub at the start of a standard module
Public TempArray() As Variant
'outside of Sub ^^
Sub SetClass1ArrayToEmpty
Erase TempArray
class1.ArrayProperty = TempArray
End Sub
the erase method will make an array empty, i used it since I occasionally used TempArray to make arrays of size 1 and wanted to make sure it was empty
5) good news, setting a range to a class property seems to work the same way as setting a range to an array, I used application.transpose(myRange) to avoid problems with one column becoming a 2 dimensional array
class1.ArrayProperty = Application.Transpose(Range(myRange))
and there you have it, here is the class that works (no compile error)
Public Property Get Contents() As Variant
Contents() = pContents()
End Property
Public Property Let Contents(Values As Variant)
'checks for an empty array being passed to it first
If IsEmptyArray(Values) Then
Erase pContents
Else
'redim pContents to size of proposed array
ReDim pContents(1 To UBound(Values))
pContents = Values
End If
End Property
Public Property Get Content(Index As Integer) As String
Content = pContents(Index)
End Property
Public Property Let Content(Index As Integer, Value As String)
Select Case Index
Case Is < 0
'Error Handling
Case Is > UBound(pContents) + 1
'Error Handling
Case Is = UBound(pContents) + 1 'append to end of array
ReDim Preserve pContents(UBound(pContents) + 1)
pContents(Index) = Value
Case Else 'replace some middle part of array
pContents(Index) = Value
End Select
End Property
Hope this helps some peeps!

Converting one type of array to another

I have one 2D array that I'm wanting to populate into another. I got a bit mixed up between lists, dictionaries and simple arrays, so I'm thinking I have two different types or array. The edited code with some attempts and resulting errors:
Dim _inifile As String = "C:\Users\Steve\Scripts\Projects\IniRecEdit\Tests\insrow.ini"
Public IniLines() As String = File.ReadAllLines(_inifile)
Public _ini(IniLines.Length - 1)() As String
For I As Integer = 0 To IniLines.Length - 1
_ini(I) = IniLines(I).Split("="c)
Next
'.....code
Dim _tempini(Lines.Length - 1, SQSIZE - 1) As String
Dim tagrow As Integer
Dim tagcol As Integer
Dim taglist() As String
Dim RowSel As Integer = 1
Dim cControl As Control
For Each cControl In Me.Panel1.Controls
If cControl.Tag <> Nothing Then
taglist = Split(cControl.Tag, ","c)
If taglist(0) = "Cell" Then
tagcol = Val(taglist(1))
tagrow = Val(taglist(2))
If tagrow <= RowSel Then
If tagcol = 0 Then
_tempini(tagrow, tagcol) = _ini(tagrow)(tagcol)
Debug.WriteLine("Lower or equal then Selected row. 1st Column. From ini to row:" & tagrow)
' EDIT etc etc... more code here
Next cControl
' DIFFERENT CODE TRIED AT THIS STAGE to transfer from one array to the other:
ReDim _ini(Lines.Length - 1)
For countrow As Integer = 0 To _tempini.GetLength(0) - 1
For countcol As Integer = 0 To _tempini.GetLength(1) - 1
_ini(countrow) = _tempini(countrow)._tempini(countcol)
Next
Next
' Produces: Error 2 Number of indices is less than the number of dimensions of the indexed array.
ReDim _ini(Lines.Length - 1)
For countrow As Integer = 0 To _tempini.GetLength(0) - 1
For countcol As Integer = 0 To _tempini.GetLength(1) - 1
_ini(countrow)(countcol) = _tempini(countrow, countcol)
Next
Next
'Produces: Additional information: Object reference not set to an instance of an object.
As I say, I'm not even sure whether I'm using lists of not for "_ini" The Locals Window on Visual Studio shows the vars as:
_ini is "String()()"
_tempini is "String(,)"
I'm increasingly aware I need to go back to basics with vb and learn all of the concepts involved. However, a quick assist would let me try and complete this thing I'm knocking together with blu-tack and string :)
You are actually using arrays. The declaration of a list would look like this:
Dim myList As New List(Of String)()
A list has always one dimension and the initial size of a list is 0 (unless you pass an enumeration to the constructor). You have to add items one by one using the Add method. This makes the list grow dynamically.
I assume that your ini-file looks like this
key1 = value1
key2 = value2
key3 = value3
Also I assume that the value does not contain an equal sign, otherwise I would look for the first instance of an equal sign an split the string there "manually".
Using a two dimensional array seems not appropriate in order to store key-value-pairs. There is a handy struct KeyValuePair(Of TKey, TValue) you could use if you need a string or a list. Using an array of KeyValuePairs has the advantage that you will not have to deal with two-dimensional arrays.
However, if you want to look up values for given keys, the Dictionary(Of TKey, TValue) is more appropriate, as it is optimized for a very fast lookup.
Dim dict As New Dictionary(Of String, String)
For i As Integer = 0 To IniLines.Length - 1
Dim parts = IniLines(i).Split("="c)
dict.Add(parts(0).Trim(), parts(1).Trim())
Next
Then you can retrive a value like this
Dim value As String
If dict.TryGetValue("some key", value) Then
' Use the value
Else
' Sorry, key does not exist
End If
Or if you are sure that the key you are looking for really exists, you can simply write
value = dict("some key")
TLDR
(1) Working with Arrays...
(2) Why are you getting "Object reference
not set to an instance of an object."
(3) Multidimensional Arrays
and Arrays of Arrays
(4) Why are you getting "Number of indices is
less than the number of dimens..."
(5) What I would do if I were
you...
Working with Arrays : Declaration(1) and Instanciation(2)
(1) declaration is when you write the name of a variable and its type. You can then access the object reffered by this variable in your code.
(2) we can talk about (variable/object) instanciation when an actual object has been created and registered somewhere in the memory. You can then manipulate that object via something that points to that object : the variable.
A declaration doesn't always mean the actual object is created. (I won't dig in ValueType and ReferenceType, sorry guys, I'm just cutting corners while explaining things without the complex verbose of strict dotNet)
Arrays declaration is different from instanciation :
Dim myArray As String()
This is just a declaration. Your Array is Nothing at this time and therefore contains nothing. Calling something like myStringVariable = myArray(0) will throw an Exception (Object reference not set to an instance of an object) meaning your array declaration points to nowhere because you haven't created an object of type String() and made your Array declaration points to that object.
Dim myArray() As String
This is the same declaration as above. Which one is correct ? I don't know and it's a matter of taste. VB is known for letting you do things the way you want. If you're good, you'll be able to use the one or the other like breathing. In fact, I really don't care.
Dim myArray As String() = new String() {}
This is just an instanciation of your array. Your array is registered in memory, but is of length 0 (zero) and contains no single item (Length = 0).
Remeber the declaration above ? (myArray() As String or myArray As String()) In fact, there is a difference in the two syntax when you talk about instanciation :
you can write : Dim myArray(3) As String
but you can't write : Dim myArray As String(3)
The later throws an exception. The variable type (String) cannot have indexes delimiters.
Dim myArray(3) As String '...
... also declares an Array and instanciate it with 4 predefined items like below.
Dim myArray As String() ' this is a declaration.
Redim myArray(3) ' this is an instanciation with a defined size.
This is one another way to define the number of items in your array using the Redim keyword. You have 4 "slots" in that Array from index [0] to index [3]. Your array looks like this :
[0][""]
[1][""]
[2][""]
[3][""]
.
Dim myArray As String = New String(3) {}
This is the same as above, but in one line : you're declaring and instanciating an Array of String that contains 4 items of type String which are all empty string (they are not Nothing)
Dim myArray() As String = { "One", "Two", "Three", "Four" }
This is a one line declaration, instanciation and items-setting :
[0]["One"]
[1]["Two"]
[2]["Three"]
[3]["Four"]
Redim
Redim is used to redifine the number of items in an Array. One dimensional arrays just behaves the way they are supposed to.
Suppose you have defined your array as :
Dim myArray() As String = { "One", "Two", "Three", "Four" }
Obviously, you have four String. Then use Redim :
Redim myArray(6) ' to get 7 items ..!
This is what you get :
[0][""]
[1][""]
[2][""]
[3][""]
[4][""]
[5][""]
[6][""]
You can resize an Array, but to keep the items inside, you'll have to use a keyword : Preserve
Redim Preserve myArray(6)
myArray(4) = "Five"
'myArray(5) = "Six"
myArray(6) = "Seven"
Because I've commented out the setting of the sixth item, the array will look like this :
[0]["One"]
[1]["Two"]
[2]["Three"]
[3]["Four"]
[4]["Five"]
[5][""] ' Empty String.
[6]["Seven"]
What happens if you Redim Preserve the Array above twice while killing some items ?
Redim Preserve myArray(2)
Redim Preserve myArray(6)
' You get :
[0]["One"]
[1]["Two"]
[2]["Three"]
[3][""]
[4][""]
[5][""]
[6][""]
You've lost items 3..6 ! Don't expect to get them back.
Public _ini(IniLines.Length - 1)() As String
What this code does ? Writing Dim myArray(3)() As String ("3" could be any positive or null integer) results in the following Array structure :
[0][Nothing]
[1][Nothing]
[2][Nothing]
[3][Nothing]
Why are you getting Nothing but not a bidimensional array ? Not even empty strings ?
It's because the declaration above creates a ONE dimensional Array of Array (of the type defined, String here)
VB.net allows you to declare an Array of Array this way (I don't know yet if C# can - but I really don't care : I never, and will never use this type of syntax) This is not a bidimensional Array. To define the values, you'll have to create each instance of the contained Array per item (or line) and assign it to your Array.
In the declaration above, while you have explicitely initialized your Array, its contents [0..3] are still simple declarations that points to no instance of anything, thus, explaining the "Nothing" thing.
Then upon getting datas from your file, your _ini variable could look like this, depending on the content of your file (that's why I asked you to provide a sample of the content of your file) :
[0][ ["Key0" | "Value0"] ]
[1][ ["Key1" | ""] ]
[2][ [""] ]
[3][ ["Key3" | "Value3" | "OtherData" | "MoreData" | "EvenMoreData"] ]
...
Reading a (String) value can be done like follows :
Dim myStringVariable As String = _ini(0)(1) ' Gets "Value0"
=> Be carefull when you use Array or Arrays along with
multidimentional arrays. They are not the same !
Let's pick the code below :
ReDim _ini(Lines.Length - 1)
For countrow As Integer = 0 To _tempini.GetLength(0) - 1
For countcol As Integer = 0 To _tempini.GetLength(1) - 1
_ini(countrow)(countcol) = _tempini(countrow, countcol)
Next
Next
Your _tempini is a bidimentional array of String.
Your _ini is an one dimensions array of array of String.
Scan this line :
_ini(countrow)(countcol) = _tempini(countrow, countcol)
1) _ini(countrow) contains Nothing (whatever the value of countrow) = contains NO Array (of String) because you've just called ReDim _ini(Lines.Length - 1) right before the For loop.
2) Therefore your assignation _ini(countrow)(countcol) = ... is the same as running the following at runtime :
Nothing(countcol) = _tempini(countrow, countcol)
Nothing is not an Array, so the (countcol) indexing has no meaning : you're indexing an item value to an object expected to be an Array that doesn't even exist.
That's why you get the Exception Object reference not set to an instance of an object.
Multidimensional Arrays / Redim / Preserve
The declaration and instanciation above still apply !
Public myBiDiArray(,) As String ' Declaration ONLY : bi dimensional array
Public myBiDiArray(2,1) As String ' Declaration AND Instanciation
Public myBiDiArray(,) As String = New String(2, 1) {} ' Dec. + Inst.
Public myBiDiArray(,) As String = _
{ {"One", "Un"}, {"Two", "Deux"}, {"Three", "Trois"} }
' ^^ Declaration + Instanciation + Item Setter
Public myBiDiArray(,) As String = _
New String(,) { {"One", "Un"}, {"Two", "Deux"}, {"Three", "Trois"} }
' ^^ Declaration + Instanciation + Item Setter
Let's have a Bidimensional Array of String with rows and columns...
Dim myArray(2, 1) As String ' 3 lines, two columns
This array contains the following items :
Line 0 = [""][""]
Line 1 = [""][""]
Line 2 = [""][""]
Now you want to resize the Array and use the Redim (only) Keyword...
Redim myArray(5, 3) ' ...
...which would set the lines to 6 and columns to 4 :
' Array Size = 6, 4
Line 0 = [""][""][""][""]
Line 1 = [""][""][""][""]
Line 2 = [""][""][""][""]
Line 3 = [""][""][""][""]
Line 4 = [""][""][""][""]
Line 5 = [""][""][""][""]
That's great !
Now let's set a value in Row(0) and Column(0)
myArray(0, 0) = "Cell,0,0"
' You get :
Line 0 = ["Cell,0,0"][""][""][""]
Line 1 = [""] [""][""][""]
Line 2 = [""] [""][""][""]
Line 3 = [""] [""][""][""]
Line 4 = [""] [""][""][""]
Line 5 = [""] [""][""][""]
But what happens with the Preserve Keyword ?
Redim Preserve myArray(3, 2) ' 4 lines and 3 columns
You get an exception : 'ReDim' can only change the rightmost dimension.
But you must know that this Exception is not handled by the debugger unless you explicitly write a Try/Catch routine that encapsulates the Redim Preserve Code. Otherwise, the application just exit the method/function containing that piece of code without any warning and your Array remains untouched !
To redim a multidimensional array (not only the last dimension) while preserving its content, you must create a new array of the desired size and copy the content of the former one to this new array, then make your former array point to that new one...
_ini(countrow) = _tempini(countrow)._tempini(countcol)
I'm not aware of such syntax in VB.Net. Perhaps you got this from another language or a Reference that adds extensions to Arrays (like System.Linq) which extends (?) the members of an Array to itself for some purpose.., but I'm not aware of that.
It looks to me like a simple syntax error. That's why you get the Exception Number of indices is less than the number of dimensions of the indexed array.
To get the (string) value of a bidimensional array, just write :
myStringValue = _tempini(countrow, countcol)
The error warning is not pointing to the systax error though : the debugger stopped at the closing backet and discarded the remaining piece of text :
_ini(countrow) = _tempini(countrow ... ' <- compiler expects a comma here.
Writing this will produce the following error :
myStringValue = _tempini(countrow, countcol)._tempini(countcol)
Compiler Error : 'tempini' is not a member of 'String'.
Again, don't mess with bidimentional arrays and arrays of arrays : Writing...
_ini(countrow) = _tempini(countrow, countcol) ' ...
... has no meaning ! Even if the syntax looks correct, the above code actually tries to assign a String value to an Array(Of String) variable, which will throw an InvalidCastException.
I'll be honest : Arrays are a great bit of fun with fixed/predefined size/dimensions. From the moment I'm working with unknown number of elements/items at runtime, I leave Arrays behind.
What I would do ?
Oh! just the same Olivier Jacot-Descombes advised you to do :) No surprise, a Dictionary is very handy in your situation.
I would also enable those two :
Option Strict On
Option Explicit On
and disable this one : Option Infer Off.
Another PERSONAL advice is to avoid using the Tag property of a control. This tag is of type Object.
some Casting should be done to get a cleaner code.
Controls are usual components you'll use anywhere. One day, I've imagined I have tied an object of type 'String' to a specific control while it actually contained a Date or even Nothing. Why ? Because I confounded two different projects and lost track of the whole thing.
pass your code to a colleague or a friend, and be sure he/she'll ask you to document it about what you've done with the Tag Property... that simply means no one want to discover the obscure underlying logic of a code involving Objects. People like to deal with strongly typed variables, easy to debug and fast performing at runtime.
Also avoid the use of Val(). Val() is very handy because it can almost convert anything. However, because of this nice feature, it adds a workload on the CPU. Again, if you know you're dealing with Int32 or Double, then use Int32.Parse() or Double.Parse() but not Val(). Most misuse of Val() on the web are related to type inference and implicit casting. Debugging such kind of glitches are a real pain. Use Val only when you have no alternative.
I think you now have almost everything you need to get your code working with little changes. I have not provided the code for the changes though :
I don't understand where your samples of code lands in your application logic
I don't know what kind of datas you're manipulating
I don't even know what's the purpose of the code.
^^ So I just addressed two things :
why you're getting errors...
since you're new to VB, I just provided a custom explanation of Arrays.
Was that the (not so) quick assist you were looking for ? I don't know. That's the best I could give right now.
OK. People have been really helpful, but not really up to totally recoding at this stage. I did find a work around, however. It aint pretty and probably is really bad practice! However, got it up and running with the code I've got.
Essentially, I just did the process in reverse, constructed a line from the components in _tempini, delimited by a "=" and then just re-ran the original procedure for populating the _ini array in the first place. A total work-around, but hope will help any other amateurs like me getting stuck in these early stages.
However, I would say follow these fellas advice! Look into list, dictionaries etc before starting your code.
The fix:
ReDim _ini(((_tempini.Length) / 2) - 1)
Dim _newiniline As String
_newiniline = ""
For countrow As Integer = 0 To (((_tempini.Length) / 2) - 1)
For countcol As Integer = 0 To 1
_newiniline = _newiniline & _tempini(countrow, countcol)
If countcol = 0 Then
_newiniline = _newiniline & "="
End If
_ini(countrow) = _newiniline.Split("="c)
Next
_newiniline = Nothing
Next

vba variant array bug

I am fairly new to excel vba and I can't seem to fix this problem with vbArrays. I created the function cumsum in vba just to make my life easier. However, I want to make the code flexible such that I can pass in both variants from a function and also a range. In my code, when I added the line vec=vec.value if I am passing in a range, it works perfectly fine but it doesn't work if I want it to work if I call the function and pass in a non range type. What I noticed was if I didn't have the line vec=vec.value in my code and I pass in a range, it has dimension 0 and I checked by writing my own function. Can someone please explain to me how I can fix this problem? Thanks.
Public Function cumsum(vec As Variant) As Variant
Dim temp() As Variant
MsgBox (getDimension(vec))
'works if i use vec=vec.value if vec is a range but has 0 if i do not vec = vec.values
ReDim temp(LBound(vec, 1) To UBound(vec, 1), 1 To 1) As Variant
Dim intCounter As Integer
For intCounter = LBound(vec) To UBound(vec)
If intCounter = LBound(vec) Then
temp(intCounter, 1) = vec(intCounter, 1)
Else
temp(intCounter, 1) = temp(intCounter - 1, 1) + vec(intCounter, 1)
End If
Next
cumsum = temp()
End Function
Function getDimension(var As Variant) As Integer
On Error GoTo Err:
Dim i As Integer
Dim tmp As Integer
i = 0
Do While True:
i = i + 1
tmp = UBound(var, i)
Loop
Err:
getDimension = i - 1
End Function
Why don't you just check the data type of vec by using VarType and TypeName then perform the necessary manipulation on vec
Public Function cumsum2(vec As Variant) As Variant
MsgBox TypeName(vec)
MsgBox VarType(vec)
cumsum2 = 0
End Function
The answers from #Jake and #chris are hints in the right direction, but I don't think they go far enough.
If you are absolutely sure that you'll only ever call this routine as a UDF (i.e. from formulas in your worksheets), then all you really need to do is add this:
If IsObject(vec) Then
Debug.Assert TypeOf vec Is Range
vec = vec.Value2
End If
to the start of your function. Called as a UDF, the only object type it should ever get passed is Range. Also, called as a UDF, you can rely on the fact that any arrays it gets passed will be indexed starting from 1.
I could pick out other problems with your routine, but they would be beside the point of your original question. Briefly: this will only work on column vectors, it will fail for single-cell ranges, etc.
Note that the reason your getDimension function is returning zero for Ranges because UBound is choking on the range. Your error handler happily catches an error (type mismatch) you didn't really expect to get and returning zero. (That method of finding "dimension" is assuming the error will be a subscript out range error.)
I wrote an answer a while back describing why, when working with Excel, I don't think the general getDimension approach is a good one:
https://stackoverflow.com/a/6904433/58845
Finally, the issue with VarType is that, when passed an object that has a default property, it will actually return the type of the property. So VarType(<range>) is going to tell you the type of the stuff in the range, not the code for object, because Range has a default property, Range.Value.
Modify your getDimension to include
If TypeName(var) = "Range" Then
var = var.Value
End If

Resources