Excel VBA constant as argument - excel

Sorry in advance, I'm a self-taught VBA programmer, and I'm not sure how to phrase my question!
I've declared constants which have similar names, i.e.
Public Const BP1Enable = "something1something:someotherthing1someotherthing"
public const BP2Enable = "something2something:someotherthing2someotherthing"
etc.
I have 10 of these constants. I have a sub with these constants as arguments:
Sub FieldEnable (ByVal enableconst)
Now I want to call the FieldEnable sub in a loop using i as counter:
For i = 1 To 10
BPfieldname = "BP" & i & "Enable"
FieldEnable enableconst:=BPfieldname
Next i
This does not work, what is happening is that the "value" assigned to enableconst in the sub FieldEnable, is "BP1Enable" instead of the value of the constant BP1Enable namely "something1something:someotherthing1someotherthing".
How can I use the variable BPfieldname when calling the sub FieldEnable?
I hope this makes sense. Any help appreciated.

Transform your variables into a single Array.
See this http://msdn.microsoft.com/en-us/library/wak0wfyt.aspx
EDIT: as #sina has correctly pointed out, VBA does not allow constant arrays,
so instead of trying this
Dim BPEnable = {
"something1something:someotherthing1someotherthing",
"something2something:someotherthing2someotherthing",
....
}
you should try this
Dim BPEnable
BPEnable = Array( _
"something1something:someotherthing1someotherthing", _
"something2something:someotherthing2someotherthing", _
"..."
)
For i = 0 To UBound(BPEnable)
BPfieldname = BPEnable(i)
Next i

The best guess would be to use a constant array, but constant arrays are not supported by VBA.
Therefore you could go with building an array out of your constants before you start your loop:
Dim BPEnable
BPEnable = Array(BP1Enable, BP2Enable)
For i = 0 To UBound(BPEnable)
FieldEnable enableconst:=BPEnable(i)
Next i
Another option would be to declare all constants as one long string with some defined separator and use a split function on that string to generate the array.

Also, if you are going to be using this "constant" in more than one place, more than one function, you can effectively make an array constant, that can even be called that way.
Public Sub Test()
For i = LBound(BPEnable) To UBound(BPEnable)
Debug.Print BPEnable(i)
Next i
End Sub
Public Function BPEnable() As Variant
BPEnable = Array("One", "Two", "Three", "Pi", "Four")
End Function
Immediate Window:
One
Two
Three
Pi
Four

Related

How do I evaluate a string mathematically? ("4*4+1/1" = 17 for example)

I've been stuck trying different methods of doing this for a while, usually not getting anywhere productive. I've tried going down each operator and splitting the string and evaluating there, I've tried looping through each character and then checking the next character but nothing seems to work very well and efficiently.
Is there a way to do this easily? I'd want to input a string, for example, "4*4+1/1" and receive an output of 17. Or, "4^2*2" = 14
The following will compute a string.
Private Sub CompteString()
Dim equation As String = "2+3"
Dim result = New DataTable().Compute(equation, Nothing)
Debug.Print(result.ToString)
End Sub

ByRef Argument Type Mismatch Excel VBA

I am using windows 7, Excel 2010, VBA. I am getting an error
"ByRef Argument Type Mismatch". I am assuming it is a problem with my variable type. I found lots of questions like mine but I can't find anything that has helped me figure out my problem.
Variable Declarations
'Force explicit variable declaration
Option Explicit
Private dptData(8) As String
Private TSdata(8) As String
Private fiscalYear(8) As String
Calling Function
parseUserData fiscalYear, dptData, TSdata
Called function Prototype
Function parseUserData(fiscalYear As String, dptDataAs String, TSdata As String)
You're passing an array to a String. Change the function's signature to accept a Variant instead.
Public Function parseUserData(fiscalYear As Variant, dptDataAs Variant, TSdata As Variant)
Debug.Assert IsArray(fiscalYear) And IsArray(dptDataAs) And IsArray(TSdata)
A String parameter can only ever accept a String argument1. Variant on the other hand, can accept anything - including an array - but then you'll want to Assert that you're dealing with an array, so that you halt execution (and prevent a bug) if that's not the case.
Why use a Variant over a typed array?
Using a typed array would work, but a typed array can't be coerced from a Variant parameter - which means this:
Public Sub DoSomething(ByRef args() As String)
...can't be invoked with this otherwise perfectly valid array of strings:
DoSomething Array("string1", "string2", "string3") ' can't pass a variant array!
Changing the signature to DoSomething(ByRef args As Variant) makes it work. All you need to do is to use a meaningful, descriptive, pluralized name to your variant array parameters, so that when IntelliSense is shown when you invoke that procedure, the name tells you everything you need to know.
But... 'Variant' is evil!
No different than many other languages, type safety in VBA is essentially smokes and mirrors. Variant is a very powerful tool and does have its uses - avoid it when you can doesn't mean unlearn its existence. Using it to pass array references around between procedures doesn't hurt the code's readability, maintainability, or stability. Variant enables duck typing and late-binding, and is a legitimate COM type.
It's a hammer. Just make sure not everything becomes a nail, and you'll do great.
1VBA will implicitly convert other value types to a String, but an array can't be coerced into a string, implicitly or explicitly.
It seems you wanted a string array after all but it may be worth mentioning that you can declare a fixed width string var.
Dim dptData As String * 8
dptData = "abc"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abc |
dptData = "abcdefghijk"
Debug.Print Len(dptData) & "|" & dptData & "|"
'result from Immediate window
'8|abcdefgh|

VBA Multiple Optional Function Parameters

I'm trying to write the SWITCH function in VBA for my coworker who has Excel 2013. I feel that my VBA is strong enough to code this function once I set up all my function parameters. However, I'm not sure how to have an unlimited number of optional parameters in a function (similar to *args in Python). How can I set up a function so that it may have an unlimited number of optional arguments?
You need to use ParamArray, e.g.
Public Function TestSum(ParamArray a())
Dim i As Long
For i = LBound(a) To UBound(a)
TestSum = TestSum + a(i)
Next i
End Function
An interesting question, here's my attempt at replicating the Switch functionality.
You will need to use a ParamArray argument:
Optional. Used only as the last argument in arglist to indicate that the final argument is an Optional array of Variant elements. The ParamArray keyword allows you to provide an arbitrary number of arguments. It may not be used with ByVal, ByRef, or Optional. (source)
Revised, thanks to the comments with #TinMan, we no longer use a Dictionary so this will be compatible with Mac OS without further tweaks.
Function FSwitch2(ValueToMatch As Variant, ParamArray ValuesToMatchAndReturn())
' example of replicating the Switch function available in Office 365, etc.
' https://support.office.com/en-us/article/switch-function-47ab33c0-28ce-4530-8a45-d532ec4aa25e
Dim i As Integer
Dim retVal As Variant
Dim default As Variant
If (UBound(ValuesToMatchAndReturn) + 1) Mod 2 <> 0 Then
' if the array is not evenly sized, assume the last argument is the default value.
default = ValuesToMatchAndReturn(UBound(ValuesToMatchAndReturn))
Else
' Otherwise, default to #N/A error if no match.
default = CVErr(2042)
End If
For i = LBound(ValuesToMatchAndReturn) To UBound(ValuesToMatchAndReturn) Step 2
If ValueToMatch = ValuesToMatchAndReturn(i) Then
retVal = ValuesToMatchAndReturn(i + 1)
Exit For
End If
Next
FSwitch2 = IIf(IsEmpty(retVal), default, retVal)
End Function

Custom Function with cell reference inputs

I'm new to excel custom functions and trying to create a custom Excel function that outputs a "Y" or "N" and takes four arguments. The error I'm getting on the other end is #VALUE! with no further explanation. I tried looking at the Range object in the MSDN docs and can't understand why my function isn't reading the values.
My function takes four arguments and the datatypes in the cells are median: Double, base: Double, hours: Integer, and exemption: String (it takes a letter "E" or "N"). I get those arguments from four separate cells in my worksheet.
Function ToIncrease(median As Range, base As Range, hours As Range, exemption As Range)
ToIncrease = "N"
exempt = exemption.Value
If exempt.Equals("N") Then
baseSal = base.Value * hours.Value
medianSal = median.Value * hours.Value
If medianSal > baseSal Then
ToIncrease = "Y"
End If
End If
If exempt.Equals("E") Then
If median.Value > base.Value Then
ToIncrease = "Y"
End If
End If
End Function
I first tried exemption.Value.Equals("N") and it didn't work, and then I tried to declare the exempt variable as Dim exempt As String = exemption.Value and got an error about expecting the end of a statement.
First replace lines like:
If exempt.Equals("N") Then
with
If exempt = "N" Then
(there may be other problems as well)
It is MUCH easier to debug VBA code as a Sub rather than a Function() because the error messages are much better.

Finding punctuation within VBA string from the right side

In VBA, how do I find the first instance of a punctuation symbol, from the right hand side? For example, from "!", I should be able to get the term "Security" two times in the following string:
INDEX(Security![a range], MATCH(J2,Security![a range],0))
Something like InStrRev would be ideal but seems like it doesnt support regex expressions. Any help is greatly appreciated!
It's true that InStrRev() doesn't accept RegEx patterns, but VBA does support RegEx and there would be a way of achieving what you need with it. However, just looping through each character and looking for any punctuation mark is pretty straightforward and might be a route you prefer.
Skeleton code (with only a few punctuation marks) below:
Public Sub RunMe()
Const punc As String = "!""*()-[]{};':#~,./<>?"
Debug.Print InStrRevAny("T:E'S!T", punc)
End Sub
Private Function InStrRevAny(refText As String, chars As String) As Long
Dim i As Long, j As Long
For i = Len(refText) To 1 Step -1
For j = 1 To Len(chars)
If Mid(refText, i, 1) = Mid(chars, j, 1) Then
InStrRevAny = i
Exit Function
End If
Next
Next
End Function

Resources