'If' statement doesn't return what it's meant to - vb.net

Am I just being blind or does this if statement genuinely not do what it's meant to?
Dim textSample as String = "F"
If Not textSample = "D" Or Not textSample = "E" Or Not textSample = "F" Then
MessageBox.Show("False")
End If
This displays the message box even though textSample is one of the letters. In my eyes that if statement should see that textSample is one of those letters and skip it, whereas if it was Z it would "Not" be equal to any of those and would therefore show the message box.
Why does it step into the if statement?

cond1 Or cond2 Or ... Or condn is true if and only if at least one of the given conditions are true. In your case it is always the case that at least one of the conditions is true (in fact at least two of the conditions will be true in each case). For example if textSample is "D" then the condition Not textSample = "E" and the condition Not textSample = "F" will be true. So the whole condition will be true.
Long story short: Use And instead of Or.

It is acting normally. True Or True Or False = True
I believe what you want is
Dim tBadLetters() As String = {"D", "E", "F"}
If Not tBadLetters.COntains(txtSample)
MsgBox("blah")
End If

It's because your using an OR clause, you need to use AND. Basically your saying if the textSample is not D then show your message box.
Change it to:
Dim textSample as String = "F"
If Not textSample = "D" AND Not textSample = "E" AND Not textSample = "F" Then
MessageBox.Show("False")
End If
That should work.

There is no value of textSample for which your if condition could possibly be false. I think you want this instead:
If Not (textSample = "D" Or textSample = "E" Or textSample = "F") Then
MessageBox.Show("False")
If you don't see the difference, examine the truth tables for both versions.

I would personally write it like so:
Dim textSample As String = "F"
If textSample <> "D" AndAlso textSample <> "E" AndAlso textSample <> "F" Then
MessageBox.Show("False")
End If
If you, like me, like to use the chainability of .NET, I also wrote for myself several String Extensions for cases like this:
Public Module StringExtensions
<Extension()> _
Public Function IsNullOrBlank(ByVal s As String) As Boolean
Return s Is Nothing OrElse s.Trim.Length.Equals(0)
End Function
<Extension()> _
Public Function IsNotNullOrBlank(ByVal s As String) As Boolean
Return s IsNot Nothing AndAlso s.Trim.Length > 0
End Function
<Extension()> _
Public Function IsEqualToAny(ByVal s As String, ByVal ParamArray values As String()) As Boolean
If s.IsNotNullOrBlank AndAlso values.Length > 0 Then
For Each value As String In values
If s = value Then Return True
Next
End If
Return False
End Function
<Extension()> _
Public Function IsNotEqualToAny(ByVal s As String, ByVal ParamArray values As String()) As Boolean
If s.IsNotNullOrBlank AndAlso values.Length > 0 Then
For Each value As String In values
If s = value Then Return False
Next
End If
Return True
End Function
End Module
To where I could then write your If statement like so:
Dim textSample As String = "F"
If textSample.IsNotEqualToAny("D", "E", "F") Then
MessageBox.Show("False")
End If

The message will always show. Here is why. In your example let us say textSample = "F". Then
if Not F equals D Or Not F equals E or Not F equals F
So let us summarize:
if (F not equals D ) or ( F not equals E ) or ( F not equals F)
...
if ( true ) or (true) or (false)
So your condition is true no matter what textSample is... (except if your textSample could be at the same be equals to "D" and equals to "E" and equals to "F").
I think you want to change the "or" to "and".

Related

Find anything but a number or "C"

I have this formula (below) where I am trying to find a space in C1. Instead of this, I would like to update this formula to look for anything except for "C" or any number and not only find a space.
LEFT(C1, find("" "", C1, 1)-1)
For e.g.
if C1 has - "C1234 - XXX" or "C1234-XXX" or "C1234:XXX", I always want the above function to find anything except for "C" and "1234" (i.e. numbers).
P.S.: I would want to use the find function only with improvements to meet the above conditions.
Please suggest.
Perhaps this:
'To create a new string from a source string which will or will not contain the characters present within the source string
'Examples of string of characters: 0123456789 -OR- {}[]<>\/|+*=-_(),.:;?!##$%^&™®©~'" OR - combination of various characters
Public Function getNewStringFromString(ByVal strSource As Variant, ByVal strChars As Variant, Optional isInString As Boolean = True) As String
Dim strArr As Variant, iChar As Variant
getNewStringFromString = ""
If VarType(strSource) = vbString And VarType(strChars) = vbString Then
strSource = Trim(strSource): strChars = Trim(strChars)
If Len(strSource) > 0 And Len(strChars) > 0 Then
strArr = Split(StrConv(strSource, vbUnicode), vbNullChar)
For Each iChar In strArr
If (isInString Xor isInArray(iChar, strChars)) = False Then getNewStringFromString = getNewStringFromString + iChar
Next iChar
Erase strArr
End If
End If
End Function
Use as the following:
MsgBox getNewStringFromString(CStr(Range("C1")), "C0123456789")
Forgot to give you the code for the isInArray function. Here it is:
'To check if an element is within a specific Array, Object, Range, String, etc.
Public Function isInArray(ByVal itemSearched As Variant, ByVal aArray As Variant) As Boolean
Dim item As Variant
If VarType(aArray) >= vbArray Or VarType(aArray) = vbObject Or VarType(aArray) = vbDataObject Or TypeName(aArray) = "Range" Then
For Each item In aArray
If itemSearched = item Then
isInArray = True
Exit Function
End If
Next item
isInArray = False
ElseIf VarType(aArray) = vbString Then
isInArray = InStr(1, aArray, itemSearched, vbBinaryCompare) > 0 'Comparing character by character
Else
On Error Resume Next
isInArray = Not IsError(Application.Match(itemSearched, aArray, False))
Err.Clear: On Error GoTo 0
End If
End Function
Given your data format, where
C is always the first character
subsequent values are all digits
You want to return the C followed by the digits
Try:
="C" & LOOKUP(9E+307,VALUE(MID(A1,2,{1,2,3,4,5,6,7})))
If there might be more than 7 digits, you can either extend the array constant, or use a formula to create a larger array.
The formula looks for the largest integer in the string, starting with position 2. So it will stop at the last non-digit, since anything including a non-digit will return an error.
If the "non-digit" might be your decimal or thousands separator, you will need to replace it with something else, with a nested SUBSTITUTE
Replace . , and space with -
=LOOKUP(1E+307,--SUBSTITUTE(SUBSTITUTE(SUBSTITUTE(MID(A1,2,{1,2,3,4,5,6,7}),",","-"),".","-"),".","-"))
For a VBA solution, I would use regular expressions.
Option Explicit
Function getCnum(str As String)
Dim RE As Object
Const sPat As String = "(^C\d+).*"
Set RE = CreateObject("vbscript.regexp")
With RE
.Global = False
.MultiLine = True
.ignorecase = True
.Pattern = sPat
getCnum = .Replace(str, "$1")
End With
End Function
Note that this also validates the string by checking that the first letter is, in fact, a C (or c). If you want it to be case-sensitive, make the obvious change.

How do I check whether value in active cell contains any letter or not?

For example cell "A1" is linked to cell "B1", so in formula bar for cell "A1" we have:
=B1
How can I check whether value in cell "A1" contains letter B?
I tried the following:
Dim Criteria_3 As Boolean
Dim Value As Range
Set Value = Selection
Dim x As Variant
Set x = Cells
Dim text As String
For Each x In Value
If IsNumeric(x) Then
Criteria_3 = VBA.InStr(1, x.Formula, text) > 0
As soon as value of "Text" is "" it does not work and I really struggle to fined the right solution.
your question is not really conclusive, so here are two options:
To check wheter the value contains B:
blnCheck = 0 < InStr(1, rngCell.Value, "B")
To check wheter the Formula contains B:
blnCheck = 0 < InStr(1, rngCell.Formula, "B")
Regarding your null string problem:
As soon as value of "Text" is "" it does not work and I really struggle to fined the right solution.
That's because you're using VBA.InStr(1, x.Formula, text) and in this case 1 is an invalid index on a string of length 0. You can omit that, or you can code around it like:
If Len(Trim(x.Formula)) = 0 Then
'## Do nothing
Else
Criteria_3 = VBA.InStr(1, x.Formula, text) > 0
End If
To your specific question of identifying when a value contains any alpha character(s):
You can use a function like this to test whether a value contains any letter, by evaluating the Ascii code for each character, and break when True:
Function ContainsAnyLetter(val) As Boolean
Dim ret As Boolean
Dim str$, ch$
Dim i As Long
str = LCase(CStr(val))
For i = 1 To Len(str)
ch = Mid(str, i, 1)
If 97 <= Asc(ch) And Asc(ch) <= 122 Then
ret = True
Exit For
End If
Next
ContainsAnyLetter = ret
End Function
In your code, you could call it like:
Criteria_3 = ContainsAnyLetter(x.Value) '## or x.Formula, depending on your needs
You can use LIKE
https://msdn.microsoft.com/en-us/library/swf8kaxw.aspx
Something like if rngCell.value like "*B*" then
if your goal is to check whether the cell contains any valid range reference, then you could go like this
Option Explicit
Sub main()
Dim cell As Range
For Each cell In Worksheets("Sheet001").Range("A1:A20") '<== jus a test range, set it as per your needs
MsgBox IsCellReference(cell.Formula)
Next cell
End Sub
Function IsCellReference(text As String) As Boolean
On Error Resume Next
IsCellReference = Not Range(Replace(text, "=", "")) Is Nothing
End Function

short way of cheeking multiple string in VBA

What is shortcut of checking string value like this.
If midtxt = "a" Then
midtxt = "apple"
ElseIf midtxt = "b" Then
midtxt = "ball"
ElseIf midtxt = "c" Then
midtxt = "cat"
.....
ElseIf midtxt = "z" Then
midtxt = "zebra"
End If
MsgBox midtxt
Is there any way I can do this using two arrays.
[a, b, c....z] and [apple, ball, cat.....zebra]
Edit
I need reproducible function for my task.
I think a for apple is not right example for me.
This is updated array for me.
[ap, bl, ca,... zr] [apple, ball, cat... zebra]
means the two letter code is derived from the corresponding string but it is not uniformly derived.
A dictionary may be worthwhile here, as long as the [a, b, ...z] set is unique.
In the VBA IDE, go to Tools, References, and select Windows Scripting Runtime.
Public gdctAnimals As Dictionary
Public Sub SetUpAnimalDictionary()
Set gdctAnimals = new Scripting.Dictionary
gdctAnimals.Add "a", "apple"
gdctAnimals.Add "b", "ball"
gdctAnimals.Add "c", "cat"
gdctAnimals.Add "z", "zebra"
End Sub
Public Sub YourProc(midtxt As String)
If gdctAnimals Is Nothing Then
SetUpAnimalDictionary
End If
If gdctAnimals.Exists(midtxt) Then
MsgBox gdctAnimals(midtxt)
Else
MsgBox "Item not found in dictionary", vbExclamation
End if
End Sub
Use the Select Case or Switch function
Function SwapString(strInput As String)
SwapString= Switch(strInput = "a", "Apple", strInput = "b", "Banana", strInput = "c", "Cherry")
End Function
In your case, if you can only have 26 combinations (a-z) the easiest way is to do this:
Public Function ReturnString(strIn As String) As String
Select Case strIn
Case "a"
ReturnString = "apple"
Case "b"
ReturnString = "ball"
Case "c"
ReturnString = "cat"
' .............
Case Else
ReturnString = "UNKNOWN"
End Select
End Function
and you call your fonction like this
MyLongString = ReturnString "a"
But there are many more possibililities that I won't detail because you have not detailed enough your question:
You can use 2 arrays or a 2D array
you can use an array of private types
you can use a dictionary as specified in another answer
No need for an external component or tedious population, you are looking for something based on an ordinal value; a=>z is the character code range 97=>122 so you can use a simple efficient array lookup by converting the character code to a value within the bounds of the array:
'//populate (once)
Dim map() As String: map = Split("apple,ball,cat,...,zebra", ",")
'//lookup
midtxt = "a"
midtxt = map(Asc(Left$(midtxt, 1)) - 97)
'=>apple
midtxt = "c"
midtxt = map(Asc(Left$(midtxt, 1)) - 97)
'=>cat
If needed check the value starts with a character first with if midtxt like "[a-z]*" then ...

VB Select Case if textbox string, elif textbox numeric (isnumeric not working)

Am using program which must treat user input differently, depends whether on number or string. Select Case and IsNumeric are not working as expected.
I get this code when animal=a char or string.
Error:
An unhandled exception of type 'System.InvalidCastException' occurred in Microsoft.VisualBasic.dll
Additional information: Conversion from string "D" to type 'Long' is not valid.
Code which troubles:
Case "D" Or "d"
All of code:
Option Explicit Off
Option Strict Off
Public Class MainForm
Public Sub ifButton_Click(sender As Object, e As EventArgs) Handles ifButton.Click
animal = codeTextBox.Text
Select Case IsNumeric(codeTextBox.Text)
Case True
Dim decanimal As Decimal
decanimal = CDec(animal)
Select Case decanimal
Case "1"
msgLabel.Text = "Dog"
Case "2"
msgLabel.Text = "Cat"
Case Else
msgLabel.Text = "Bird"
End Select
Case False
Dim stranimal As String
stranimal = CStr(animal)
Select Case stranimal
Case "D" Or "d"
msgLabel.Text = "Dog"
Case "C" Or "c"
msgLabel.Text = "Cat"
Case Else
End Select
End Select
End Sub
End Class
You should look at the documentation for Select Case you don't put Or, you put a comma.
Case "D", "d"
Or put the compared string in lower case.
Select Case stranimal.ToLower()
Case "d"
msgLabel.Text = "Dog"
Case "c"
msgLabel.Text = "Cat"
Case Else
End Select
decanimal is a decimal, don't use string in your case statement. Also, turn that option strict on ;)
You could use the return value of Double.TryParse() to see if the data is numeric. Here's the corrected code:-
Public Sub ifButton_Click(sender As Object, e As EventArgs) Handles ifButton.Click
Dim animal As String = codeTextBox.Text
Select Case Double.TryParse(animal, Nothing) 'See if it is convertible to Double (numeric) or not.
Case True
Select Case Double.Parse(animal)
Case 1
msgLabel.Text = "Dog"
Case 2
msgLabel.Text = "Cat"
Case Else
msgLabel.Text = "Bird"
End Select
Case False
Select Case animal.ToLower() 'To compare the strings without case-sensitivity
Case "d"
msgLabel.Text = "Dog"
Case "c"
msgLabel.Text = "Cat"
Case Else
'You didn't mention anything but I guess it should be msgLabel.Text = "Bird"
End Select
End Select
End Sub
An alternative coding approach...
Dim cAnimals As New Collection
cAnimals.Add("Dog", "d")
cAnimals.Add("Dog", "1")
cAnimals.Add("Cat", "c")
cAnimals.Add("Cat", "2")
Dim s As String = ""
Do While True
s = InputBox("code:").ToLower
If s = "" Then Exit Do
If cAnimals.Contains(s) Then
MsgBox(cAnimals(s))
Else
MsgBox("Invalid code")
End If
Loop
Use some data structure to store codes for conversion, here a VB Collection, then check to see if the code is in the data or not.
VB doesn't really care if the data is numeric or not. This can bite at times but is useful at other times. Option Strict will defeat, not really needed IMO for most cases. Depends on the importance of the app and locally policy.

weird IF statement error " Conversion from string "FAIL" to type 'Long' is not valid."

I'm basically running through rows in a table. I'm checking each row to see if at least 1 of the columns im interested is = "pass" and that the rest = either "pass" or "N/A".
But im getting an error:
"An exception of type 'System.InvalidCastException' occurred in Microsoft.VisualBasic.dll but was not handled in user code
Additional information: Conversion from string "FAIL" to type 'Long' is not valid."
The statuses can either be "PASS", "FAIL", OR "N/A" in the DB is you're wondering where the FAIL came from.
Here's the code:
Private Function HasAuditPassed(activeLotId As String) As Boolean
Dim sql = String.Format("SELECT STEPID, CAVITYPAIRASTATUS,CAVITYPAIRBSTATUS,CAVITYPAIRCSTATUS,CAVITYPAIRDSTATUS FROM " & _
"TB_BL_AMMSSTEPSTATUS WHERE AUDITLOT = '{0}'", activeLotId)
Dim dynaset = DB.CreateDynaset(sql, DBWrapper.DynasetOptions.ORADYN_READONLY)
Do Until dynaset.EOF
Dim cavityPairAStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRASTATUS")
Dim cavityPairBStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRBSTATUS")
Dim cavityPairCStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRCSTATUS")
Dim cavityPairDStatus = dynaset.GetFieldData(Of String)("CAVITYPAIRDSTATUS")
If (cavityPairAStatus Or cavityPairBStatus Or cavityPairCStatus Or cavityPairDStatus) = "PASS" And
(cavityPairAStatus And cavityPairBStatus And cavityPairCStatus And cavityPairDStatus) = ("PASS" Or "N/A") Then
Return True
End If
Loop
Return False
End Function
Sadly, you can't do if statements that way. You have to write the entire statement out because VB .Net does not handle string comparison that way. So a statement that would be the equivalent and work would be much longer and look like this:
If (cavityPairAStatus = "PASS" OrElse cavityPairBStatus = "PASS" OrElse
cavityPairCStatus = "PASS" OrElse cavityPairDStatus = "PASS")
And (cavityPairAStatus = "PASS" OrElse cavityPairAStatus = "N/A" AndAlso
cavityPairBStatus = "PASS" OrElse cavityPairBStatus = "N/A" AndAlso
cavityPairCStatus = "PASS" OrElse cavityPairCStatus = "N/A" AndAlso
cavityPairDStatus = "PASS" OrElse cavityPairDStatus = "N/A") Then
Return True
End If
A few more elegant solutions with creating lists are below, and can be used to help out readability some if you have to write statements like this repeatedly.
There are a couple of issues here. You can't write a If stytement in this style and you can't use binary operators (And and Or) with String.
Your If statement should be something like this:
If (cavityPairAStatus = "PASS" OrElse cavityPairBStatus = "PASS" OrElse …
If you want something a little more elegant you may consider something like this:
Dim values = {cavityPairAStatus, cavityPairBStatus, cavityPairCStatus, cavityPairDStatus}
If values.Any(Function(v) v.Equals("PASS")) AndAlso values.All(Function(v) v.Equals("PASS") OrElse v.Equals("N/A")) Then
…
End If
The way IF statement you have used is incorrect.
To compare so many conditions you need to write them separately.
Instead of using IF condition here I would suggest you to use a list.
Here's the sample code
Dim cavityPairStatus As New List(Of String)
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRASTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRBSTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRCSTATUS"))
cavityPairStatus.Add(dynaset.GetFieldData(Of String)("CAVITYPAIRDSTATUS"))
If cavityPairStatus.Contains("PASS") OrElse cavityPairStatus.Contains("N/A") Then
Return True
End If