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"))
I am trying to distinguish between no input or "Null" and the input of something including the number 0.
I wrote a public function called "ZeroToAppear" that works well enough when used with Index Match Functions by returning the number 0 as a string, but it will not work along with a sum function which is common in financial budgets:
Public Function ZeroToAppear(x As Variant) As Variant
If IsNull(x) Then
ZeroToAppear = Null
ElseIf x = 0 Then
ZeroToAppear = CStr(x)
Else
ZeroToAppear = x
End If
End Function
I have rationalized that the problem is that excel automatically considers null as a 0 in order to avoid ArgumentNullExceptions.
So I am trying to write another Macro that will work when taking the sum of a range that can distinguish between no input and 0 or greater input since the sum of cells with no input automatically equals zero in excel and I would like it to report null or even better report false in order to not do the sum at all.
I have started writing a function that tests each cell in the range that I would be summing to see whether it is null, error, or something. If it is null or error, I want it to report null into a test array. If there is some other input I want it to report whatever that input is into the test array. Then I want to identify if the entire test array is reporting null to make my original function false & not run the sum in the range that I am testing but if there are other values then the function should return true and the sum can be run.
Public Function NullOrErrorFalse() As Variant
Dim arrOutput() As Variant
ReDim arrOutput(n) As Variant
n = 0
For Each cell In NullOrErrorFalse()
If IsNull(cell) Then
arrOutput(n) = Null
ElseIf IsError(cell) Then
arrOutput(n) = Null
Else
arrOutput(n) = cell.Value
End If
n = n + 1
Next cell
Sub test(arrOutput())
If arrOutput() = Null Then
NullOrErrorFalse = False
Else
NullOrErrorFalse = True
End If
End Sub
End Function
At this point my function won't compile correctly and being new to VBA and programming in general, I am not sure if my issue is misuse or syntax or order of operations.
Yep, there is programming flaw due to misunderstanding VBA syntax.
Public Function NullOrErrorFalse() As Variant
Dim arrOutput() As Variant
....
For Each cell In NullOrErrorFalse()
NullOrErrorFalse is the name of the function, not the parameter you are examining. When changing it, you are actually changing the result. On the other hand, you need to provide your function with the parameter (range) that it will check.
You can do you custom sum as a User-Defined Function (UDF) in this way:
Public Function SumOrNull(r As Range) As Variant
SumOrNull = 0
For Each cel In r
If IsError(cel) Or cel = "" Or Not IsNumeric(cel.Value) Then
SumOrNull = CVErr(xlErrValue)
Exit Function
End If
SumOrNull = SumOrNull + cel.Value
Next
End Function
The idea is that as soon as there is any pattern that you dont want, your UDF raises an error, so that Excel displays #Value, and the cell using your UDF is considered as erroneous.
I have 2 functions that check to see if a string exists in an array.
I don't know which is better and if there are any reasons to use one over the other. Any help would be appreciated. Thank you :)
function 1
Function IsInArray(stringToBeFound As String, arr As Variant) As Boolean
IsInArray = (UBound(Filter(arr, stringToBeFound)) > -1)
End Function
function 2
Function IsInArray(myArray As Variant, val As String) As Boolean
Dim i As Integer, found As Boolean
found = False
If Not Len(Join(myArray)) > 0 Then
found = False
Else
For i = 0 To UBound(myArray)
If myArray(i) = val Then
found = True
End If
Next i
End If
IsInArray = found
End Function
This is what I would do.
For each thing in Arr
If instr(Thing, StringToBeFound) > 0 then msgbox thing
Next
Your second function uses a lot of memory with a big array.
This is what happens when you join strings. I don't know if Join function uses stringbuilding or not. I doubt it does as nothing else in basic does. So ordinary concatination shuffles a lot of bytes around memory.
String Concatination
And don't join strings one character at a time. See this from a VBScript programmer. It requires 50,000 bytes and many allocation and deallocation to make a 100 character string.
http://blogs.msdn.com/b/ericlippert/archive/2003/10/20/53248.aspx
PS: We have the Mid statement in VBA (not to be confused with the Mid function) that allows string building as a generic solution to shuffling bytes. It only matters on large arrays/strings.
Both solutions should work. The first one is small and straightforward, and moreover it returns how many times the match was found.
The second solution has the advantage of being "under your control", since you know what exactly you are doing. For example, with a slight modification you can return the position of the first match. You can make it faster though by adding the statement "Exit For" after "found = true", since there is no need to check further...
Thanks to several people who input in this thread I was able to create this solution
Function IsInArrayyyy(stringToBeFound As String, arr As Variant) As Boolean
For Each thing In arr
If InStr(thing, stringToBeFound) > 0 Then
If Len(thing) = Len(stringToBeFound) Then 'remove this if exact match not needed
IsInArrayyyy = True: Exit Function
End If 'remove this if exact match not needed
End If
Next
End Function
I am trying to pass an array to a UDF to avoid massive duplication of code. As a simple example:
function USERFUNC1(inp as variant)
Dim array_size As Integer
dim i as integer
dim values as double
array_size = WorksheetFunction.CountA(inp)
for i = 1 to array_size
values = values + inp(i)
Next i
USERFUNC1 = values
End function
function USERFUNC2(input1 as variant, input2 as variant)
Dim array_size As Integer
dim i as integer
dim values as double
array_size = WorksheetFunction.CountA(input1)
redim nested_array(array_size)
for i = 1 to array_size
nested_array(i) = input1(i)+input2(i)
Next i
USERFUNC2= USERFUNC1(nested_array)
End function
In the example I have create I have the nested array that I am passing internally to the UDF. However when run this results in a by ref error. I am sure that this can be done but I appear to be missing something
EDIT
It seems what I wrote caused confusion, essentially I have a number of functions, the above is just to demonstrate the idea.
On some of the cells I am calculating a value using a function (call it fugacity) that picks up values into an array a range from the worksheet. In another function (phase equilibrium) I need to perform the same calculation (fugacity) within the second function (phase equilibrium) using values calculated within the second function. This requires me to pass the array from the second function into the first or write out the entire first function again within the second.
I could do that however it make the 2nd function much more difficult to debug as I can no longer be certain that the nested calculation is performing the right calculation, rather I need to check the whole thing. So far I have about 250 lines of code in the first and 300 line the second and within the second (phase equilibrium) I need to perform the first (fugacity) 4 times.
If you want USERFUNC1 to return an array of the values in the inp range, then merely:
function USERFUNC1(inp as Range) as Variant
USERFUNC1 = inp
End function
This will be a 1-based two dimensional array where the first dimension represents the rows, and the second dimension the columns in the original range inp.
I'm not sure what your end goal is, but you may not even need USERFUNC1
I am trying to populate a two dimensional array of ranges. I don't know how big the array will need to be, so I am using the ReDim and Preserve functions to dynamically re-size the array as required.
I am encountering runtime error 91: "Object variable or With block variable not set" when I run the code.
I am not an experienced coder, but I have managed to isolate the error, and am sure it is coming from the pseudo code below.
Can anyone see any mistakes I have made that would produce the runtime error?
Dim ArrayName() as Range
Dim counter as Integer
If condition = True Then
counter = counter + 1
ReDim Preserve ArrayName(0, counter - 1)
ArrayName(0, counter - 1) = Cells(counter, counter) 'I get a runtime error here
End If
Thank you.
If you want to store ranges within your array you need to add Set before problem line in this way:
Set ArrayName(0, counter - 1) = Cells(counter, counter)
But if you want to store values of the cells you need to change declaration line to this:
Dim ArrayName() as Double 'or String or Variant depending on value type you need to keep in array