Considerations when using Function name as a variable - vba

In VBA, you can treat a UDF name just like a Dimmed variable. For example
Function ADD(a, b) As Long
ADD = a
ADD = ADD + b
End Function
Where ADD stores the intermediate value as well as the end result. My question is; in terms of what's going on behind the scenes, is there any difference between storing a number/piece of data in a standard variable vs a function name variable.
I worry that perhaps the routine that called the function gets an update whenever the variable changes if you use the function name, rather than only when End Function is executed. IE. if you have some code
answer = ADD(1, 2)
then in memory answer gets written to twice, once when ADD = a, once when ADD = ADD + b. Of course we don't see this, because answer is left with whatever the final ADD value is
I ask because I often find I build up a function answer in stages using an intermediate variable, then pass that to the function name itself, where instead I could just write directly to the function name.
E.g.
Function ADD(a, b) As Long
Dim tot As Long
tot = a
tot = tot + b
ADD = tot
End Function
vs the first example. They acheive the same thing, in the second example tot represents the formula result, and so we need a final ADD = tot line at the end. For speed I would like to reduce any writes that are made, so is there any drawback in terms of speed, not readability to using the first method as opposed to declaring intermediates?
NB, to clarify, that's not all intermediates, I just mean the single intermediate that represents the function result, and could be replaced by the function name in the code.

In speed the first method should be slightly faster - you declare one variable less (but I doubt that someone would be able to notice it).
In general, using the first method can bring you to a recursion, if you are not careful (or if you are a VBA beginner):
Option Explicit
Dim lng10 As Long
Public Sub Test()
lng10 = 0
Debug.Print ADD2(1, 1)
End Sub
Function ADD2(a As Long, b As Long) As Long
lng10 = lng10 + 1
ADD2 = a + b
If lng10 < 10 Then
ADD2 = ADD2 + ADD2(1, 1)
End If
End Function
And if the recursion does not have a bottom, it would go to an overflow error.
With other words, this would be an runtime error:
Option Explicit
Dim lng10 As Long
Public Sub Test()
lng10 = 0
Debug.Print ADD2(1, 1)
End Sub
Function ADD2(a As Long, b As Long) As Long
lng10 = lng10 + 1
ADD2 = ADD2(a, b)
End Function

Related

Speed up simple user defined functions

User defined functions writen in Excel VBA seem to run much slower than functions simply written in the worksheet cells. Is there a way to run them faster? As an example I have a very simple user defined function:
Function myweekday(mydate As Double)
myweekday = Weekday(mydate)
End Function
Basically it does the same thing as the built in =weekday() function. However running this on 50,000 cells takes about 5 seconds to calculate, whereas simply using the built in function takes a fraction of a second.
What can I do to make user defined functions like this run faster?
You can instead pass in the values as a range and have it return all the values at once in an array. I just tried this on 50,000 rows and it returned all the values instantly. You will need to enter the UDF with CTRL + SHIFT + ENTER.
Function myweekday(mydate As Range) As Variant
Dim vMydate As Variant
Dim vMyWeekDay As Variant
Dim i As Long
vMydate = mydate.Value2
ReDim vMyWeekDay(1 To UBound(vMydate), 1 To 1)
For i = 1 To UBound(vMydate)
vMyWeekDay(i, 1) = Weekday(vMydate(i, 1))
Next i
myweekday = vMyWeekDay
End Function

Count characters between two empty space to dashes() in vba

How do I get the length of character between beginning with space and ending with * Here is the image. Column B shows the total len before dasher(-) and my code
Sub xn()
Dim x As Integer
x = 1
If Worksheet("Sheet1").Range("A"& x).len(Right," ") Or _
Worksheet("Sheet1").Range("A"&x)len(Left,"-") Then
len(totallen)
End If
x = x + 1
End Sub
The code posted has multiple issues:
Worksheet is not a valid object - you need to use Worksheets.
.len is not a property of a Range object.
Even in .len was a property of a Range, you would need a
de-reference operator (aka '.') in here: Range("A"&x)len(Left,"-")
If you intend to use the function Len(), it only takes one argument.
You apparently are trying to loop, but you need to use either a For
or For Each loop - it won't loop automatically when you increment x
at the bottom of the sub.
Right is a function, but you're calling it without arguments and they are not optional.
Similarly, Left is a function, but you're also calling it without
the required arguments.
totallen is not declared anywhere, so Len(totallen) will assume
that totallen is a Variant (default for undeclared variables), then
cast it to a String, and then always return 0 because it has never
been given a value.
Anything else I may have missed.
The solution is to use the InStr function. It returns the location in a string of a given sub-string.
Sub xn()
Dim x As Long
Dim sheet As Worksheet
Set sheet = ActiveWorkbook.Worksheets("Sheet1")
For x = 1 To sheet.Range("A" & sheet.Rows.Count).End(xlUp).Row
sheet.Cells(x, 2) = InStr(1, sheet.Cells(x, 1), "-") - 1
Next x
End Sub
I'd also recommend taking a look at the MSDN article on Looping Through a Range of Cells (2003 vintage, but still valid), and Error Finding Last Used cell In VBA.

how i can figuring the highest number in my array...visual basic

how i can figure the highest number in my array...below is the code...can someone help me to solve my problems...n i wan to show the result in the label from the other windows form....thank u... :
Public Class Frm2
Public Parties(9) As String
Public Votes(9) As String
Dim vote As Integer
Dim Party As String
Party = TParty.Text
vote = TVote.Text
For I As Integer = 0 To Parties.Length - 1
If Parties(I) = "" Then
Parties(I) = TParty.Text()
For J As Integer = 0 To Votes.Length - 1
If Votes(J) = "" Then
Votes(J) = TVote.Text()
MsgBox(TParty.Text & TVote.Text & " votes")
TParty.Clear()
TVote.Clear()
Exit Sub
End If
Next J
End If
Next
MsgBox("you can vote now")
If you want to use an algorithm to find the highest number into an array (let's say Votes), the classic is coming from the so-called Bubble Sort:
Dim max As Long 'change the type accordingly, for example if votes are 1-10 then Integer is better
max = Votes(0) 'set the first vote as the max
For j = 1 To Votes.Length - 1
If Votes(j) >= max Then max = Votes(j) 'if another element is larger, then it is the max
Next j
Now the variable max stores the highest value of the array Votes, that you can show anywhere as, for example, in MyForm.MyLabel.Text = max. More useful info here.
Please note that now you declare Public Votes(9) As String, which means they are strings so not usable as numbers. You might want to declare them with a different data type, or use the CInt() method to convert strings in integers as suggested by ja72.
I thought this would only work with a Variant array, but in quick testing it seems to work with an array of Longs as well:
Dim Votes(9) as Long
Dim Max As Long
Max=WorksheetFunction.Max(Votes)
Note that, as Matteo says, you should change Votes() to an array of numeric types. I'd use Long, as it's a native VBA type.
EDIT: As noted by Dee, the code in this question is actually VB.Net. I added that as a tag. In VBA the solution would be even simpler, as Max is an array property:
Max=Votes.Max
(I suppose it would be a good idea to change the variable name from "Max".)

VBA shorthand for x=x+1?

Sub btn1_Click()
Static value As Integer
value = value + 1
MsgBox value
End Sub
I swear when I was taking a VB.net course in college there was a shorter way to tell a variable to add '' to itself. Maybe x=+1. I am using Access now though instead of visual studio. When I try that within the VBE it removes the +. I even removed Option Explicit with no change
Assuming the answer will be no, there is no way to short-hand it & its just a peculiarly of VBA
Sadly there are no operation-assignment operators in VBA.
(Addition-assignment += are available in VB.Net)
Pointless workaround;
Sub Inc(ByRef i As Integer)
i = i + 1
End Sub
...
Static value As Integer
inc value
inc value
If you want to call the incremented number directly in a function, this solution works bettter:
Function inc(ByRef data As Integer)
data = data + 1
inc = data
End Function
for example:
Wb.Worksheets(mySheet).Cells(myRow, inc(myCol))
If the function inc() returns no value, the above line will generate an error.

Functions not actualizing

I execute a VBA code that takes a database, treats it and export it into a sheet. This is working fine. However, I have a sheet that produces graphs depending on the data in the particular sheet. The datas does not actualize. I have to enter the cell and click enter to actualize it. I'm pretty sure there is an easier way to do this. Calculation is set to automatic but that doesn't seem to change anything.
In my cell, I have my own vba function that needs to be updated once the report is done. When I click the cell and then enter, the result is updated but I would like this to be done automatically. I hope this is clearer !
Thanks in advance,
Etienne NOEL
HEre is the code of my function
Public Function number_of_appearances(term As String, sheet As String, column As Integer) As Integer
Application.Volatile
Dim number_of_rows As Integer
Dim appearances As Integer
Dim row As Integer
appearances = 0
row = 1
number_of_rows = Worksheets(sheet).UsedRange.Rows.Count
Do While row <= number_of_rows
If Worksheets(sheet).Cells(row, column).Value = term Then
appearances = appearances + 1
End If
row = row + 1
Loop
number_of_appearances = appearances
End Function
A cell example of a user of the function
=number_of_appearances('test';'sheet1'; 3)
Sounds like your UDF might not depend on any cells that change value when your DB is processed.
See This MSDN Link
Post your UDF (or just its header if you prefer) and an example of its use...
EDIT:
Yes, none of the parameters to the UDF are cell references, therefore the UDF is not triggered to recalculate when data on the shet changes.
You have two choices:
1. rewrite your UDF to include parameter(s) that reference cells that change value when the DB is processed
2. make your UDF volitile (include Application.Volatile in the UDF code) WARNING: this can be very inefficient, depending on how many time the UDF is used and how intensive its calculation is
EDIT 2:
Heres a refactor of your udf using the first option mentioned:
Public Function number_of_appearances(term As String, rng As Range) As Integer
Dim v As Variant
Dim i As Long, j As Long
Dim appearances As Long
v = Intersect(rng, rng.Worksheet.UsedRange)
For j = LBound(v, 2) To UBound(v, 2)
For i = LBound(v, 1) To UBound(v, 1)
If v(i, j) = term Then
appearances = appearances + 1
End If
Next i, j
number_of_appearances = appearances
End Function
use like
=number_of_appearances("test";Sheet1!C:C)
EDIT 3:
If all you are doing is counting number of occurances of a string in a range, consider using
=COUNTIF(Sheet1!C:C;"test")