Excel VBA: Failed to pass a string array to a Function - vba

VBA Beginner here.
I am trying to pass an array of strings from a subroutine to a function which will then modify each string in the array. However I get the "Type:array or user-defined type expected" error message.
I have tried redefining different data types for the array so it is aligned with the data type entered in the function but to no avail.
Hope you can help! THank you so much!
Below is the dummy code:
Sub text()
Dim haha() As Variant
haha = Array("Tom", "Mary", "Adam")
testing (haha())
MsgBox Join(haha, " ")
End Sub
Function testing(ByRef check() As String) As String()
Dim track As Long
For track = LBound(check) To UBound(check)
check(track) = check(track) & " OMG"
Next
End Function

In orignial code, a string is not the same variant (I believe they both would need to be variant? someone can verify), you dont need the brackets after testing, only need brackets if you are setting to another value e.g.
haha2 = testing(haha())
Below code should be ok
Sub text()
Dim haha()
haha = Array("Tom", "Mary", "Adam")
testing haha()
MsgBox Join(haha, " ")
End Sub
Function testing(ByRef check()) As String
Dim track As Long
For track = LBound(check) To UBound(check)
check(track) = check(track) & " OMG"
Next
End Function

You have a few errors in your code:
There are two ways of invoking methods:
1) with Call keyword - in this case you must give all the parameters in brackets:
Call testing(haha)
2) without Call keyword - in this case you just give your parameters after the name of function:
testing haha
In your code you combined both of them and this is syntax error.
If you pass an array as a parameter to function you don't need to put brackets like that: testing (haha()).
The proper syntax is:
testing(haha)
Function testing requires as a parameter an array of String type, you cannot pass object of other type instead since it causes compile error Type mismatch. Currently you are trying to pass variable haha which is of Variant type.
You can change the type of haha variable to array of strings (to avoid the error described above):
Dim haha() As String
However, in this case you cannot assign the value of function Array to it, since the result of this function is of Variant type.
You would have to replace this code:
haha = Array("Tom", "Mary", "Adam")
with this:
ReDim haha(1 To 3)
haha(1) = "Tom"
haha(2) = "Mary"
haha(3) = "Adam"

A couple of suggestions to improve your code:
Dim haha() As String
You define the type of the entry in the array, not the array itself. Use the way mentioned by mielk to fill the array.
Function Testing(byref check as variant) As String
This will avoid problems with undefined variables. Not clear why you feel that the function should return a string though. Maybe even convert to a Sub instead.

Related

VBA Function Passing Multi Variables back to Sub

I have a large string over 500 char which is called strEssay. I want to use a function(since I will need to look for several patterns) to return two values if (for example the name) Frank is found or not.
This is the function I'm trying to use:
Function NameFinder(strEssay as String, strName as String)
Dim varNameCounter as Variant
Dim strNameFinderResult as String
varNameCounter = 0
strNameFinderResult = ""
If strEssay like "*" & strName & "*" Then
strNameFinderResult = strName
varNameFinderCounter = 1
Else
strNameFinderResult = ""
varNameFinderCounter = .001
EndIf
End Function
I want to be able to return back to my subroutine both 'strNameFinderResult' and 'varNameFinderCounter'.
Is there any way that I can return both values?
If I can't return both simultaneously can I return one through the function and the other through a textbox or something? What would calling the function look like in the subroutine and/or how would I need to change my function?
NameFinder() function, returning array of 3 elements. It is called and returned by TestMe(), writing the following to the console:
Function NameFinder(essay As String, name As String)
Dim nameFinderResult As String
Dim namefinderCounter As String
nameFinderResult = "" & essay & name
namefinderCounter = 0.001 + 12
NameFinder = Array(nameFinderResult, namefinderCounter, "something else")
End Function
Public Sub TestMe()
Dim myArray As Variant
myArray = NameFinder("foo", "bar")
Dim i As Long
For i = LBound(myArray) To UBound(myArray)
Debug.Print myArray(i)
Next i
End Sub
As a general rule, you have to give the routine a type like this:
Function NameFinder(strEssay as String, strName as String) as string
But, that returns only ONE value.
So, a function (as opposed to a sub) returns one value (as a general rule).
However, you CAN also return parameters that you pass. I mean, in above, you can't make TWO assignments to one variable, can you?
So, you can use a Sub like this:
Sub NameFinder(strEssay as String, strName as String, _
strNameFinderResult as string, _
varNameFinderCounter as double)
If strEssay like "*" & strName & "*" Then
strNameFinderResult = strName
varNameFinderCounter = 1
Else
strNameFinderResult = ""
varNameFinderCounter = .001
EndIf
So in code, you now can go:
dim strMyResult as string
dim strFinderCount as Double
Call NameFinder("MyEassy", "Joe Blow", strMyResult, strFinderCount)
So, you can return values with the parameters.
Now, I suppose it possible for some strange reason, that you want to use a function to return two values with a single assignment?
What you would do is this in your code module.
Define a custom type, and use that.
eg this:
Option Compare Database
Option Explicit
Type SearchResult
strName As String
FindCount As Double
End Type
Function NameFinder(strEssay As String, strName As String) As SearchResult
NameFinder.FindCount = 0
NameFinder.strName = ""
If strEssay Like "*" & strName & "*" Then
NameFinder.strName = strName
NameFinder.FindCount = 1
Else
NameFinder.strName = ""
NameFinder.FindCount = 0.001
End If
End Function
So, now to use in code? You can go like this:
dim MyResults as SearchResult
MyResults = NameFinder("My eassy", "Joe Blow")
debug.print "Name found result = " & MyResults.strName
debug.print "Count of find = " & MyResult.FindCount
The VERY nice thing about above is you get full intel-sense in your code editor.
eg this:
So by building a custom data type, you can use "one" assignment for the return type. And you get nice type checking and inteli-sense in the VBA code editor.
And you can even do this:
But, to get both variables, then you would in theory wind up calling the function two times. So, you can actually use the function without declarer of variables like this:
Debug.Print NameFinder("MyEassy", "Joe blow").strName
Debug.Print NameFinder("MyEassy", "Joe blow").FindCount
So, I don't recommend the above, but in the case in which you ONLY want one of the return values, then the raw expression (function) like above would be a use case (and no need to even declare a return variable).
But, without a doubt, define a custom type in code as per above. The reason is now you get a really nice VBA editor type-checking, inteli-sense, and also that you only have to declare "one" variable that holds two values.
In fact, the results are very much like JavaScript, or even c# in which you declare a "class" type. So with a custom "type" you are declaring a data type of your own. And the beauty of this is if you need say 3 values, then once again you create a type with 3 "inside" values.
The you ONLY have to declare that one variable as the custom type.
With this you get:
Very valuable compile time syntax and data type checking of the var types you are using.
You get GREAT VBA inteli-sense while coding - which means less coding mistakes.
And you type far less typing in the VBA editor as it will pop-up the choices for you as you write code. And you can't type or choose the wrong sub - type, as the compiler will catch this.

VBA Multi-Dimensional Arrays - Array Literal Syntax

I want to create a multi-dimensional array where I assign all the values at once instead of going through all the array coordinate values one by one. I believe this is called setting 'array literals'. Anyway, all my variables are string values. The code below doesn't give me a syntax error but when I step through I'm getting a "Compile error: Can't assign to array" message on pkg= line. How do I make this work?
Sub test_array2()
Dim pkg(2, 2) As String
pkg = [{"PRetail","Retail Packaged"};{"PFoodservice","Foodservice
Packaged"}]
Debug.Print pkg(1, 1)
End Sub
You cannot assign directly to an array like that, so you need to use a variant:
Sub test_array2()
Dim pkg As Variant
pkg = [{"PRetail","Retail Packaged";"PFoodservice","FoodservicePackaged "}]
Debug.Print pkg(1, 1)
End Sub

Using a function to clean data in VBA

I am familiar with this post: How to Return a result from a VBA Function but changing my code does not seem to help.
I want to write a simple function in VBA that allows to lowercase an input sentence. I wrote this:
Private Function Converter(inputText As String) As String
Converter = LCase(inputText)
End Function
Sub test()
Dim new_output As String
new_output = Converter("Henk")
MsgBox (new_output)
End Sub
I tried following the advice I found at another stackoverflow post. I made me change this:
Private Function Converter(inputText As String)
Set outputText = LCase(inputText)
End Function
Sub test()
Dim new_output As String
Set new_output = Converter("Henk")
MsgBox (new_output)
End Sub
However, now I get an error that an object is required. Why does it require an object now? I dont get it...
Set outputText = LCase(inputText)
The Set keyword is reserved for Object data types. Unlike VB.NET, String in VBA is a basic data types.
So you dont Set a variable to a string. Drop the second version of your code altogether. It doesn't make sense. That "advice" was probably in another context.
To fix your first version
1- Assign the returned result to the name of the function Converter
2- It would be beneficial to specify explicitly the return type, as String. Currently it is a Variant that always embeds a String, so better make it explicit:
' vvvvvvvvv
Private Function Converter(inputText As String) As String
Converter = LCase(inputText) ' <------------ assign return to name of function
End Function

ByRef argument type mismatch in Excel VBA

I'm working with VBA. I wrote a user define function that takes a string, process it and return a cleaned string. I am not sure what is wrong with it. I am not able to call it and ask it to process my string and return it. I am thinking there are a mistake in the way I am defining or returning it.
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
And I use this function like this
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
Last name is a string variable, usually looks like this Lastname*****, and I am trying to remove all the stars behind it. Have it return Lastname without the stars.
I received Compile error: ByRef arugment type mismatch when I tried to run this. I am using Windows XP with Office 2003.
EDIT: I added the basic struction of the code I have, I have about 20 lines of the similar code. Doing the same thing for each field I need.
Private Sub CommandButton2_Click()
' In my original production code I have a chain of these
' Like this Dim last_name, first_name, street, apt, city, state, zip As String
Dim last_name As String
' I get the last name from a fixed position of my file. Because I am
' processing it from another source which I copied and pasted into excel
last_name = Mid(Range("A4").Value, 20, 13)
' Insert the data into the corresponding fields in the database worksheet
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
I suspect you haven't set up last_name properly in the caller.
With the statement Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
this will only work if last_name is a string, i.e.
Dim last_name as String
appears in the caller somewhere.
The reason for this is that VBA passes in variables by reference by default which means that the data types have to match exactly between caller and callee.
Two fixes:
1) Force ByVal -- Change your function to pass variable ByVal: Public Function ProcessString(ByVal input_string As String) As String, or
2) Dim varname -- put Dim last_name As String in the caller before you use it.
(1) works because for ByVal, a copy of input_string is taken when passing to the function which will coerce it into the correct data type. It also leads to better program stability since the function cannot modify the variable in the caller.
I don't know why, but it is very important to declare the variables separately if you want to pass variables (as variables) into other procedure or function.
For example there is a procedure which make some manipulation with data: based on ID returns Part Number and Quantity information. ID as constant value, other two arguments are variables.
Public Sub GetPNQty(ByVal ID As String, PartNumber As String, Quantity As Long)
the next main code gives me a "ByRef argument mismatch":
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty, BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
and the next one is working as well:
Sub KittingScan()
Dim BoxPN As String
Dim BoxQty As Long
Dim BoxKitQty As Long
Call GetPNQty(InputBox("Enter ID:"), BoxPN, BoxQty)
End sub
I changed a few things to work with Option Explicit, and the code ran fine against a cell containing "abc.123", which returned "abc.12,". There were no compile errors.
Option Explicit ' This is new
Public Function ProcessString(input_string As String) As String
' The temp string used throughout the function
Dim temp_string As String
Dim i As Integer ' This is new
Dim return_string As String ' This is new
For i = 1 To Len(input_string)
temp_string = Mid(input_string, i, 1)
If temp_string Like "[A-Z, a-z, 0-9, :, -]" Then
return_string = return_string & temp_string
End If
Next i
return_string = Mid(return_string, 1, (Len(return_string) - 1))
ProcessString = return_string & ", "
End Function
I'll suggest you post more of your relevant code (that calls this function). You've stated that last_name is a String, but it appears that may not be the case. Step through your code line by line and ensure that this is actually the case.
While looping through your string one character at a time is a viable method, there's no need. VBA has built-in functions for this kind of thing:
Public Function ProcessString(input_string As String) As String
ProcessString=Replace(input_string,"*","")
End Function
Something is wrong with that string try like this:
Worksheets(data_sheet).Range("C2").Value = ProcessString(CStr(last_name))
It looks like ByRef needs to know the size of the parameter. A declaration of
Dim last_name as string
doesn't specify the size of the string so it takes it as an error. Before using
Worksheets(data_sheet).Range("C2").Value = ProcessString(last_name)
The last_name has to be declared as
Dim last_name as string *10 ' size of string is up to you but must be a fix length
No need to change the function. Function doesn't take a fix length declaration.
For me the problem here was that I was declaring multiple variables in a row instead of separate rows.
For example, I was trying to pass i as an integer to my function.
Dim i,j as integer - gets me the error
Dim i as integer - doesn't get the error

object required error on function call

I made a Class Module and named it Time. I wanted to have a function in it that created an instance of the Time object and returned it. See code below. If you can think of a better way to write the function, I'm all ears. The problem I'm having, as it stands is when I make the following statement:
tsheet.MondayStart = Time.Construct(Item.Value)
A debug.print statement prior to this call shows that Item.Value is "08:30".
tsheet is of type TimeSheet and MondayStart property is expecting to be assigned an object of type Time. However, when this statement executes at runtime, I get the object required error. I even tried removing the paranthesis, but all that does is bring up another error "Compile Error: Expected end of statement"
How do I fix this? Please advise. Too bad vba doesn't support the notion of construtors :-(
Alan
'IN TIME CLASS MODULE
Public Function Construct(Value As String) As Time
'This function expects a string formatted like: 08:30
'Time.Delimiter returns ":"
Dim tempTime As Time
Dim vhours As Integer
Dim vminutes As Integer
Dim arrTime() As Time
arrTime = Split(Value, Time.Delimiter)
hours = CInt(Trim(arrTime(0)))
minutes = CInt(Trim(arrTime(1)))
Set tempTime = New Time
tempTime.hours = vhours
tempTime.minutes = vminutes
Construct = tempTime
End Function
Actually I suggest you use the already implemented VBA methods DateValue and TimeValue which will accept a string and give you what you need.
I am not sure you need to re-invent the wheel here. Of course I might have missed something so please let me know.
Use the result of the new as follows:
Set MonStart = New TimeFrame
Set tsheet.MondayStart = MonStart.Initialize(MonStart, Item.Value)
(For comparision, here's previous:
Set MonStart = New TimeFrame
Set tsheet.MondayStart = TimeFrame.Initialize(MonStart, Item.Value)
)
Also use Set on function return value assignment.
for the first code example:
...
Set Construct = tempTime
End Function
and for the other code example:
...
Set Initialize = Value
End Function