How To Check If A Variable From Double Type Doesn't Have A Value. VB.NET - string

my problem in VB.NET.
I hope you see my code.not all the my code is important.
the important part is in the second if condition and which is if snum = "".
the snum variable is a variable from double type and i check if this variable is empty. i mean it doesn't have any value.
and when i run the code , the program show me this error on the second if condition which i told you about.
""Additional information: Conversion from string "" to type 'Double' is not valid.""
I work on a program to move motors. and the code which i wrote is one of the things that i do in the program.
why i check if snum = "" ??
because the value of snum is the coordinates of one of the motors and it is Z Axis Motor. and the coordinates are written in TextBox.
i took the coordinates of Z Axis Motor from the TextBox and put it in snum variable. then i check if the snum variable is empty by this code if snum == "". if snum is empty then the Z Axis Motor will not move. and if snum is not empty and even if its value is 0 the Z Axis Motor Will Move.
so how can i check if a variable from double type doesn't have a value ?
enve if its value is 0 the motor will move.
i want to check if it doesn't have any value.
this is the code.
the procedure works on button click
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim snum As Double
Dim tnum As Double
Dim h As Integer = 0
If h = 1 Then
snum = TextBox1.Text
tnum = TextBox1.Text
TextBox3.Text = snum
TextBox4.Text = tnum
Else
If snum = "" Then
snum = tnum
TextBox2.Text = "Hello , How Are You"
TextBox3.Text = snum
TextBox4.Text = tnum
End If
End If
End Sub

Your code still makes little sense as it is but I'll answer in a more general sense about value types. Double is a value type and therefore ALWAYS has a value. When you declare a variable, under the hood, the memory allocated to that variable will contain all zeroes by default and that is represented by Nothing in VB. What that actually means depends on the type. For all reference types (i.e. classes) it represents no object. For all value types (i.e. structures) it represents the default value of that type, e.g. False for Boolean, #1/01/0001# for Date and zero for all numeric types. That means that, in your case, your Double variables will have the value 0.0.
If you want to be able to represent no value for a value type then you have to use nullable value types, e.g. Nullable(Of Double) or Double? for short. You can test the HasValue property of a nullable value type to see whether it has a value and, if it does, get that value from the Value property. For a Nullable(Of T)/T?, the Value property will be type T. Note that getting the Value property when HasValue is False will throw an exception.
Dim d1 As Double?
Dim d2 As Double?
If d1.HasValue Then
Console.WriteLine("d1 = " & d1.Value)
Else
Console.WriteLine("d1 has no value")
End If
If d2.HasValue Then
Console.WriteLine("d2 = " & d2.Value)
Else
Console.WriteLine("d2 has no value")
End If
d1 = 0.0
d2 = 123.456
If d1.HasValue Then
Console.WriteLine("d1 = " & d1.Value)
Else
Console.WriteLine("d1 has no value")
End If
If d2.HasValue Then
Console.WriteLine("d2 = " & d2.Value)
Else
Console.WriteLine("d2 has no value")
End If

Jmcilhinney is right: this code does not have much use. However here is how to convert the text variables to double and vice versa with CDBL () and Tostring:
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim snum As Double
Dim tnum As Double
Dim h As Integer = 0
If h = 1 Then
snum = cDbl(TextBox1.Text)
tnum = cDbl(TextBox1.Text)
TextBox3.Text = snum.toString
TextBox4.Text = tnum.toString
Else
If snum = 0 Then
snum = tnum
TextBox2.Text = "Hello , How Are You"
TextBox3.Text = snum.toString
TextBox4.Text = tnum.toString
End If
End If
End Sub

Related

Trying to generate random numbers in VBA, code returning only values that already exist in the table

I cannot get what this is doing.
For a data entry work, I need to generate a transaction number for each entry.
I have a simple function that returns a string created from a random number:
Public Function GetNewTnum() As String
Randomize 10
GetNewTnum = "SL-" & Int(Rnd * 1000000)
End Function
When I call the function in the sub, not only is it generating the same number everytime it runs, the number can already be found in the data set, which only has 577 entries at the moment!
In the sub, after generating the string, it checks to see if has already been used and another function returns true/false. A loop keeps testing until the generated string has not been found and uses this in the entry:
Public Function finddupTnum(ByRef num As String) As Boolean
Dim f As Range
Set f = inputws.Range("tblDataMaster[Tnum]").Find(num, , xlValues, xlWhole)
If f Is Nothing Then
finddupTnum = False
ElseIf Not f Is Nothing Then
finddupTnum = True
End If
End Function
Loop structure works fine. And even before the code reaches the loop, the first random number that gets generated is as described above:
Dim i As Integer
For i = 1 To 7
If Not Me.Controls("Product" & i) = "" Then
With newrow
newtnum = GetNewTnum()
MsgBox "First tnum: " & newtnum
valb = finddupTnum(newtnum)
MsgBox "Found? " & valb
Do While valb = True
newtnum = GetNewTnum()
valb = finddupTnum(newtnum)
MsgBox "New tnum: " & newtnum
MsgBox "New tnum dound? " & valb
Loop
The loop can't end because all the values generated already exist.
Thank you for your help!
In VBA there is the need to use Randomize without a fixed number (the seed). Otherwise it will always generate the same numbers starting from that seed.
https://learn.microsoft.com/de-de/office/vba/language/reference/user-interface-help/randomize-statement
I removed the Randomize statement and now its generating random numbers.
Also, I changed the loop struture. Now instead of finding generating a new random number, it simply adds 1 the first one created, searches and iterates.
I think at least this way, it will never find a duplicate:
newtnum = GetNewTnum()
valb = finddupTnum(newtnum)
Do While valb = True
newtnum = newtnum + 1
valb = finddupTnum(newtnum)
Loop
And changed the function to make it the right data type ;)
This should work replacing your two functions - first to get random number, then check the current column for duplicates
Option Explicit
Const START_NUM As Long = 1
Const END_NUM As Long = 1000000
Const RANGE_COL As String = "A"
Const RANGE_NUMS As String = RANGE_COL & START_NUM & ":" & RANGE_COL & END_NUM
Public Function GetRandomNumber()
Dim NewNum As Double
Dim NumEntries As Long
Dim NumTries As Long
NewNum = Application.WorksheetFunction.RandBetween(START_NUM, END_NUM)
NumEntries = Application.WorksheetFunction.CountA(Range(RANGE_NUMS))
NumTries = 1
While (Application.WorksheetFunction.CountIf(Range(RANGE_NUMS), NewNum) > 1) And (NumTries <= NumEntries)
NewNum = Application.WorksheetFunction.RandBetween(START_NUM, END_NUM)
DoEvents
NumTries = NumTries + 1
Wend
Debug.Print NewNum
End Function

How to pass a string with parameters for evaluation in Excel?

In VBA, is there a way to create a function which receives one of it's parameters as a string with a condition to be evaluated by an IF block?
This should give an idea of what I am looking for, but I now it's not that simple:
Function StringAsCondition(a As String) As Boolean
Dim result As Boolean
Dim b As Long
Dim c As Long
b = 4
c = 2
If a Then
result = True
End If
StringAsCondition = result
End Function
Sub Test()
Dim a As String
a = "b >= c"
Dim functionresult As Boolean
functionresult = StringAsCondition(a)
MsgBox functionresult
End Sub
The evaluation of the string is actually a rather easy task in the "fancy" programming languages (all but VBA). There, you would simply use some type of string formatting, replace and evaluate.
In VBA, you may build your own string formatting (credits Is there an equivalent of printf or String.Format in Excel) and use it:
Sub Test()
Dim condition As String
Dim b As Long, c As Long
b = 4
c = 2
condition = "{0} >= {1}"
Debug.Print Application.Evaluate(StringFormat(condition, b, c))
End Sub
Public Function StringFormat(mask As String, ParamArray tokens()) As String
Dim i As Long
For i = LBound(tokens) To UBound(tokens)
mask = Replace$(mask, "{" & i & "}", tokens(i))
Next
StringFormat = mask
End Function
My answer just tries to present the real solution (of #Vitiata) in a way to directly answer the request:
Function StringAsCondition(cond As String) As Boolean
Dim result As Boolean
Dim b As Long
Dim c As Long
b = 4
c = 2
result = CBool(Application.Evaluate(StringFormat(cond, b, c)))
StringAsCondition = result
End Function
Sub TestEV()
Dim a As String
Dim functionresult As Boolean
a = "{0} >= {1}" 'instead of a = "b >= c"
functionresult = StringAsCondition(a)
MsgBox functionresult
End Sub
Private Function StringFormat(mask As String, ParamArray tokens()) As String
Dim i As Long
For i = 0 To UBound(tokens)
mask = Replace$(mask, "{" & i & "}", tokens(i))
Next
StringFormat = mask
End Function
Allow placeholding variables in any order
Just in addition to #Vityata 's clever solution, a similar solution with a mask definition closer to common mathematical formulae with variables in any order.
So an expression (condition) of e.g.
y >= x*c would be transformed easily to a mask string like
expression = "{y} >= {x}*{c}"
Sub TestStringFormat()
' Purp: evaluate mask with place holding variables in any order
Dim expression As String
expression = "{y} >= {x}*{c}" ' "17 >= 4*2" ~~> True
Dim myVars As String: myVars = "c,x,y" ' "c,x,y", c x y
Debug.Print Application.Evaluate(StrFrm(expression, myVars, 2, 4, 17))
End Sub
Public Function StrFrm(mask As String, myVars As String, ParamArray tokens()) As String
' Purpose: mask with place holding variables in any order ' e.g. y >= x*c
' Note: modified by T.M.; credit to https://stackoverflow.com/users/246342/alex-k
' Site: https://stackoverflow.com/questions/17233701/is-there-an-equivalent-of-printf-or-string-format-in-excel
Dim vars: vars = Split(myVars, ",") ' variables c,x,y
Dim i As Long
For i = LBound(vars) To UBound(vars)
mask = Replace$(mask, "{" & vars(i) & "}", tokens(i))
Next
StrFrm = mask
Debug.Print StrFrm
End Function

How to use string names in loop like form controls?

In form controls we can use { controls("Textbox"&1) } as for loops.
My question is I have already defined the String as D1,D2,D3. I want to use like D as common and suffix as variable
sub abcd ()
dim i, k as integer
dim D1 as string
dim D2 as string
k="abcd1"
for i = 1 to 2
if k<> "" then 'like controls("textbox" & i ) for loop
"D"&i = true
Else "D" & i+1
end sub
It shows a messagebox with the error:
expected : line number or label or statement or end of statement
This code has several (actually a lot) of issues:
Dim i, k As Integer declares k As Integer but i As Variant in VBA you need to specify a type for every variable.
You declare D1 as String but True is a Boolean.
If you declare Dim D1 As String you cannot access the variabele with "D" & i = True. This is no valid syntax. Therefore you would need to use an array:
Dim MyArray(1 To 2) As Boolean
So you can access it like
MyArray(i) = True 'where i can be 1 or 2
If you declare k As Integer that means k repersents a number (without decimals). So you cannot put text in there k = "abcd1", this will produce a type missmatch. Also comparing k against a string (text) "" like If k <> "" Then will missmatch since k is a number.
Also have a look at data type summary to study which data type to use for which kind of data.
Your For i = 1 To 2 loop is missing a Next i in the end of the loop.
Your If statement is missing a End If in the end.
So in general I can only recommend you to study some tutorials or books about the basic syntax of VBA, you are lacking a lot of basic things.
So the following example might help you:
Option Explicit
Public Sub Test()
Dim i As Long
Dim MyArray(1 To 3) As Boolean
For i = 1 To 3
MyArray(i) = True
Next i
End Sub

how to not enter if statement inside a loop if it have been executed

I have a for loop, and inside it i have if statement.
In my Excel I have a list that contains each value one time. Once I found it i don't want the code to even check the conditional, i want it to skip this part of the if statement completely each time the loop is executed, is it possible?
Here is my code and list:
the first iteration of the loop will find that "c" is the value so it will do what inside it (xc = i)
I don't want the code to even check "ElseIf Cells(1, i) = "c" again, like the following image, is this possible?
code as text:
Sub test()
Dim i, xa, xb, xc As Integer
For i = 1 To 5
If Cells(i, 1) = "a" Then
xa = i
ElseIf Cells(i, 1) = "b" Then
xb = i
ElseIf Cells(i, 1) = "c" Then
xc = i
End If
Next i
End Sub
My initial interpretation of your need was "if the code hits 'c' again, just don't act".
To do so, you could modify the logic as follows:
ElseIf (xc = 0) And (Cells(i, 1) = "c") Then
This way, as soon as xc is set, the first boolean expression would be False, and the overall condition would not ever be met again. As mentioned by #TimWilliams, VBA would still evaluate the second boolean expression, unlike other languages that feature short-circuiting options. #Gene's answer describes a way around this. Typically, for better performance, you would evaluate the simple conditions first, before resorting to costly ones.
Additional notes
In VBA, you must give a type to each variable. In your Dim line, only xc is an Integer, while the other variables are Variants.
An unqualified Cells() call operates on the currently active worksheet, which might not be the expected one. Suggestion: qualify Cells() with the CodeName of your worksheet. The CodeName is what you see or specify under a worksheet's (Name) property as seen from the Visual Basic editor. For example, if (Name) is Sheet1, use Sheet1.Cells(). This will only work if the code resides in the same workbook as Sheet1. If the code is behind the worksheet itself, you can even use Me.Cells().
When dealing with cell values as your code does, VBA is (silently) being nice and understands that, among the numerous properties of the Range class, Value is what you are interested in. It is better practice, however, to explicitly state the target property, such as in Sheet1.Cells(i, j).Value.
EDIT
Knowing the values will be distinct and that there are about 60 of them, I suggest you simply use a Dictionary, as shown below, to get each value's row in one go, without a cascade of Ifs:
Option Explicit
Sub test()
Dim i As Integer
Dim dict As Object 'Scripting.Dictionary
Set dict = CreateObject("Scripting.Dictionary")
For i = 1 To 5
dict(Cells(i, 1).Value) = i
Next
Debug.Print dict("a") '4
Debug.Print dict("b") '2
Debug.Print dict("c") '1
'Etc.
End Sub
if i understood your question you can try this code:
Sub test()
Dim i, xa, xb, xc As Integer
Dim a, b, c As Boolean
a = False
b = False
c = False
For i = 1 To 5
If Cells(i, 1) = "a" And a <> True Then
xa = i
a = True
ElseIf Cells(i, 1) = "b" And b <> True Then
xb = i
b = True
ElseIf Cells(i, 1) = "c" And c <> True Then
xc = 1
c = True
End If
Next i
End Sub
Boolean variable is setted true for example only when the cells(i,1)="a" and after the next "a" value are skipped...
hope this helps
I just wanted to "mod" Ferdinando's code so it's a bit more "readable", I think. The main (the substantive) difference between this version and Ferdinando's or Excelosaurus' is that the cell is not even tested once the value is detected. Remember that the question was: I don't want the code to even check "ElseIf Cells(1, i) = "c" again... So, this version does exactly that.
Sub test()
Dim i As Integer, xa As Integer, xb As Integer, xc As Integer
Dim aFound As Boolean, bFound As Boolean, cFound As Boolean
Dim r As Range
For i = 1 To 5
Set r = Cells(i, 1)
If Not aFound Then
If r = "a" Then xa = i: aFound = True
ElseIf Not bFound Then
If r = "b" Then xb = i: bFound = True
ElseIf Not cFound Then
If r = "c" Then xc = i: cFound = True
End If
Next i
End Sub
I don't like the idea of 60 ElseIfs. Please examine the code below. In order to test it, create a worksheet called "TestSheet" and enter your A1:A5 to cells H2:H6.
Sub TestSpike()
' 06 Jan 2019
Dim Rng As Range
Dim Items As Variant
Dim Spike As String
Dim Tmp As String
Dim i As Integer
Dim R As Long
Items = Split("c|b|0|a|1", "|")
With Worksheets("TestSheet").Columns("H")
For R = 2 To 6
Tmp = CStr(.Cells(R).Value)
If InStr(1, Spike, Tmp, vbTextCompare) = 0 Then
Spike = Spike & "|" & Tmp
On Error Resume Next
i = Application.WorksheetFunction.Match(Tmp, Items, 0)
If Err Then
MsgBox Tmp & " wasn't found in Array"
Else
MsgBox "i = " & i & " = Item " & Tmp
End If
End If
Next R
End With
End Sub
The code has a "Spike". Each item is first checked against the Spike. If it is found there no further tests are carried out. Else, it is added to the Spike.
New items, after being added to the Spike, are checked against the Array "Items" which would hold your 60 elements, separated by Chr(124) thus, Split("c|b|0|a|1", "|"). I use the worksheet function MATCH to look for the item in the array. The result is an index number (or an error, if not found). You can use this index number in a Select Case statement to process each item distinct from others, basically the same way as you now process it when the If statement returns True.
One idea you may find useful with this kind of setup is to use the index from the Match function to return a value from another array. The other array might, for example, contain function names and you use Application.Run to call a different function for each item. This would run significantly faster than examining 60-odd Select Case statements.

How to remove letters from end of ID string (variable letter amount)?

I have a list of ID's that I'm trying to clean and compare with another list. The ID's have variable formatting (e.g. RFP322343BA, PPL232334, RFP32334A-00). I'm trying to standardize the data on the front-end (e.g. RFP322343, PPL232334, and RFP32234) to allow for comparison. How can I remove these end text/symbol strings of varying length?
With RFP32334A-00 in cell A1, then
=IF(RIGHT(LEFT(A1,9),1)="A",LEFT(A1,8),LEFT(A1,9))
works, assuming 1) only the first 9 chars are of interest and 2) it is only ever "A" in the 9th place for the "odd" number you provided above. If there are only a few of these odd ones, then just left(a1,9) will be simpler.
Consider the following User Defined Function (UDF):
Public Function FirstPart(sIn As String) As String
Dim i As Long, L As Long, Armed As Boolean, CH As String
FirstPart = ""
Armed = False
L = Len(sIn)
For i = 1 To L
CH = Mid(sIn, i, 1)
If IsNumeric(CH) Then
Armed = True
End If
If Not Armed Then
FirstPart = FirstPart & CH
Else
If Not IsNumeric(CH) Then
Exit Function
Else
FirstPart = FirstPart & CH
End If
End If
Next i
End Function
It locates the first non-numerical character after the first numerical character and clips the string at that point.

Resources