why is the index of the one-dimensional array changing this way? - arrays

I'm a beginner and I'm encountering a problem with an index of a one-dimensional array of strings (m_nameList in the code) that I'm struggling to understand. I have one method (in a class of a Windows Form Application that is not the MainForm) that returns FALSE if a string of the array at a certain index is empty, and viceversa. It looks like this:
Public Function IsReserved(index As Integer) As Boolean
Dim reserved As Boolean = False
If (Not String.IsNullOrEmpty(m_nameList(index))) Then
reserved = True
Else
reserved = False
End If
Return reserved
End Function
I have also a method on the MainForm that calls for it, and if the first method returns true then the second one displays a dialog box, otherwise it doesn't (lstResults is a listbox that has for items the strings of the array):
Private Function CheckIfSeatIsAlreadyReserved() As Boolean
Dim reserved As Boolean = m_seatMngr.IsReserved(lstResults.SelectedIndex)
If (reserved) Then
Dim msgBox As Integer = MessageBox.Show("The seat is already reserved. Would you like to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
If (msgBox = DialogResult.Yes) Then
Return True
Else
Return False
End If
Else
Return True
End If
End Function
The problem I find is: if as in the code of the first method above I don't initialize index to any value, the second method will work perfectly, but I'll get a runtime error if I don't select any item on the listBox (and therefore index = -1). On the other hand, if I initialize index to 0 or to m_nameList.Length, then the program won't have any problems if I don't select any item on the ListBox and it will nicely display a warning message I wrote for that case, BUT the second method will behave in a weird way: it will find ALL the strings of the array full even if I only "write" the first one, or it will find ALL the strings empty if I "write" any string that it's not the first one (by writing I mean that I call for a method that puts my input as the text of the string selected). Any idea on why this happens and how I can fix it? Thank you so much.

I will get to the answer below, follow the logic, lets look at your first method. You can refactor it to
Public Function IsReserved(index As Integer) As Boolean
Return Not String.IsNullOrEmpty(m_nameList(index))
End Function
Everything else is called "noise". But as Fabio pointed, what if index is bad? Lets add useful code
Public Function IsReserved(index As Integer) As Boolean
If index < 0 OrElse index > m_nameList.Length - 1 Then
Throw New ArgumentOutOfRangeException(". . . . .")
End If
Return Not String.IsNullOrEmpty(m_nameList(index))
End Function
If you get this exception, your program has a bug - and you know to fix the consumer of this function.
And now lets get to your message
second method will behave in a weird way: it will find ALL the strings of the array full even if I only "write" the first one, or it will find ALL the strings empty if I "write" any string
Most likely reason why it happens - simply because you create condition when you send same value over and over again. Therefore result is same. And this is most likely a bug
If (reserved) Then
. . . . . .
Else
Return True ' <---- BUG ALLERT
End If
And two more things, your second method can shrink down to 1 line too.
Private Function CheckIfSeatIsAlreadyReserved() As Boolean
Return m_seatMngr.IsReserved(lstResults.SelectedIndex) AndAlso _
DialogResult.Yes = MessageBox.Show("The seat is already reserved. Would you like to continue?", "Warning", MessageBoxButtons.YesNo, MessageBoxIcon.Question)
End Function
And, if you do some sort of reservation, array is not really good idea. How about Dictionary(Of String, Boolean)? Your theater or airplane has bunch of seats and you pre-load your dictionary with those
d.Add("1A", False)
d.Add("1B", False)
d.Add("1C", False)
d.Add("1D", False)
d.Add("1E", False)
d.Add("1F", False)
This is your whole airplane row. Now you can do much more with it

Related

Converting a returned array from an async function to a regular array

It's been a while since I've written absolutely any code at all so I'm a little rusty. I've recently started writing a bot and am currently stuck on converting an array within an async function to a regular array to be parsed within another function.
Private Async Function getmsgs(ByVal num As Integer) As Task(Of Array)
Dim msgids As Array = Nothing
Try
Dim msg = Await Libx.DownloadMessages(num)
'starting loop to pull each message and assign to array
For i As Integer = msg.Count - 1 To 0 Step -1
If (msg(i).Id > 0) Then
msgids(i) = msg(i).Id
End If
Next
'returning array
Return msgids
Catch ex As Exception
Return msgids
End Try
End Function
Private Sub deletemsgs(ByVal ids As Array)
If Not ids(0) = Nothing Then
'incase library failed to retrieve messages
Try
For i As Integer = ids.Length - 1 To 0 Step -1
'function called per item in array
libx.DeleteMessages(ids(i))
Next
Catch ex As Exception
End Try
End If
End Sub
The issue occurs when trying to call deletemsgs(getmsgs(argint)).
I'm getting the error message
Value of type 'Task(Of Array)' cannot be converted to 'Array'
I've searched through a few forums with every relating search term I can think of. Any help would be greatly appreciated, cheers in advance.
You need to await async functions.
An example being:
Dim arr As Array() = await getmsgs(1)
This will need to be inside of an async function for it to work correctly.

How to retrieve a VB Control instance, given its Name and Index?

If I have a string "cboEnoughMoney(0)", is it possible to retrieve the control from a control array that I have in my form, cboEnoughMoney?
In my code, I try to refer to the control in this way:
frm.Controls(sControlName).ListIndex = -1
sControlName in this case is "cboEnoughMoney(0)" and I get an error that the control was not found.
EDIT: One of the issue's i'm having is trying to as above set the .listindex to -1, another one is where I actually try to get and set value to the combobox. In a function - i pass form name as parameter called frm.
In the code what has been done is this....
dim ctlData as string
If Frm.Controls(RS!CTRLname).ListIndex > 0 Then
CtlData = Frm.Controls(RS!CTRLname).Value
Else
CtlData = ""
End If
Any idea how I'd be able to do that with cboEnoughMoney(0)....
I assume I could follow your example and check
if instr(1,RS!CTRLname, "(")
but if there is, how would I refer to that particular control in frm
It is just enough to look in the VB Properties Window: if you search "cboEnoughMoney(0)" you will find "cboEnoughMoney"(0) (without double quotes). Still the name is the same as a non-array control.
So, it is perfectly legal to refer to a VB Control with Name and Index, no need for a controls loop here:
If isControlsArray Then
Set ctrl = Me.Controls(Name)(Index) '// array
Else
Set ctrl = Me.Controls(Name) '// non array
End If
You cannot use the (index) part in a lookup via .Controls.
You can loop them manually & given that you can safely assume any control name with a ( is an array member and thus will have an Index property match on that:
Private Function GetControl(ByVal name As String) As VB.Control
Dim pos As Long: pos = InStr(name, "(")
If pos = 0 Then
Set GetControl = Me.Controls(name) '// non array
Else
Dim index As Long
index = Val(Mid$(name, pos + 1)) '// get index #
name = Left$(name, pos - 1) '// get base name
For Each GetControl In Me.Controls
If (GetControl.name = name) Then
If (GetControl.index = index) Then Exit Function
End If
Next
Set GetControl = Nothing
End If
End Function
And then:
GetControl("cboEnoughMoney(1)").ListIndex = -1

Inserting NULL integer using VB.Net and EF5

Working on an application that relies on an older version of entity, and I'm trying to insert a NULL into an int field. The field in SQL Server is (int, null).
Here's the definition of the object in EF:
<EdmScalarPropertyAttribute(EntityKeyProperty:=false, IsNullable:=true)>
<DataMemberAttribute()>
Public Property application_id() As Nullable(Of Global.System.Int32)
...and here is where I'm trying to set it:
applications.application_id = IIf(IsNumeric(txtAppID.Text), CInt(txtAppID.Text), Nothing)
The error thrown in response is:
An exception of type 'System.InvalidCastException' occurred in ... but was not handled in user code
Additional information: Specified cast is not valid.
I can confirm that this issue is being thrown due to the Nothing portion because previously it was applications.application_id = CInt(txtAppID.Text) and all was fine.
I've tried DBNull.Value instead of Nothing, though the error reads the same. Done a fair bit of research though most issues relate to ES6 or datetime fields, and as such I felt my issue was specific enough to warrant its own question.
Thanks.
The IIf function doesn't short circuit, and therefore always evaluates both the true and false parts, so it's not going to work in that situation. The If keyword does short circuit, but you will probably run into issues with the return type and nullable value types (e.g. Dim x As Integer? = If(False, 1, Nothing) results in x = 0 because the If is returning Integer and not Integer?).
So, I would recommend either using a regular If statement:
If IsNumeric(txtAppID.Text) Then
applications.application_id = CInt(txtAppID.Text)
Else
applications.application_id = Nothing
End If
or you could create a helper function:
Function NullableCInt(value As String) As Integer?
If IsNumeric(value) Then Return CInt(value)
Return Nothing
End Function
and use that:
applications.application_id = NullableCInt(txtAppID.Text)
You can get working If method with casting
Dim temp As Integer
applications.application_id = If(Integer.TryParse(value, temp), temp, DirectCast(Nothing, Integer?))
For better readability you can introduce "default" value
Static DEFAULT_VALUE As Integer? = Nothing
Dim temp As Integer
applications.application_id = If(Integer.TryParse(value, temp), temp, DEFAULT_VALUE)
With Integer.TryParse you need "check/convert" string to integer only once.

Remove duplicate items from processes array using VB .net

I have to code to get all accessible process, but I need to remove duplicated items on this array and show only one time each process.
How is the best method to do this, because I think processes array is not like a normal array.
My code:
For Each p As Process In Process.GetProcesses
Try
'MsgBox(p.ProcessName + " " + p.StartTime.ToString)
Catch ex As Exception
'Do nothing
End Try
Next
Thanks in advance
The Process.GetProcesses() method returns an array. You can use the Distinct method, providing an IEqualityComparer to it.
An example would be as comparer:
Public Class ProcessComparer
Implements IEqualityComparer(Of Process)
Public Function Equals1(p1 As Process, p2 As Process) As Boolean Implements IEqualityComparer(Of Process).Equals
' Check whether the compared objects reference the same data.
If p1 Is p2 Then Return True
'Check whether any of the compared objects is null.
If p1 Is Nothing OrElse p2 Is Nothing Then Return False
' Check whether the Process' names are equal.
Return (p1.ProcessName = p2.ProcessName)
End Function
Public Function GetHashCode1(process As Process) As Integer Implements IEqualityComparer(Of Process).GetHashCode
' Check whether the object is null.
If process Is Nothing Then Return 0
' Get hash code for the Name field if it is not null.
Return process.ProcessName.GetHashCode()
End Function
End Class
And you can use it like this:
Sub Main()
Dim processes As Process() = Process.GetProcesses()
Console.WriteLine(String.Format("Count before Distinct = {0}", processes.Length))
' Should contain less items
Dim disProcesses As Process() = processes.Distinct(New ProcessComparer()).ToArray()
Console.WriteLine(String.Format("Count after Distinct = {0}", disProcesses.Length))
Console.ReadLine()
End Sub
You probably have to refine the Comparer to your specifications and for the situation you are going to use it.

visual basic array case statement

I'm new to visual basic and have been using vb.net to create a console/text based game. I want to make my game a little bit more "smart". The idea I've had to do this is to create an array of synonyms for yes and an array of synonyms for no, and similar arrays for over words. I intended on using a case statement with the array to decide weather the users input was a synonym for yes or for no. I have had no luck so far, and I was wondering if anybody here knew how it can be done or if I'm barking up the wrong tree. Maybe there is a different way for me to approach this?
My Select attempt:
Select Case userInput
Case yes(0) To yes(34)
Console.WriteLine("you said something like yes, you said {0}", userInput)
End Select
The start of my array: (there are 34 synonyms so far)
Dim yes(0 To 34) As String
yes(0) = "yes"
yes(1) = "ok"
yes(2) = "yep"
yes(3) = "yeah"
If anybody can help me it would be very much appreciated :) Thank you very much!
You do not have to use a Select Case for this purpose. A simple List(Of T).Contains can do the trick. Then you can go ahead to put it in a function so you can call it several times in your application.
Sample Code:
Public Enum Answer
Yes
No
Other
End Enum
Public Function GetAnswer(answer As String) As Answer
Static yesAnswers = New List(Of String)({"yes", "yeah", "yep", "aye"})
Static noAnswers = New List(Of String)({"no", "nope", "nay"})
If yesAnswers.Contains(answer.ToLower()) Then
Return Answer.Yes
ElseIf noAnswers.Contains(answer.ToLower()) Then
Return Answer.No
Else
Return Answer.Cancel
End If
End Function
I would create a translation dictionary
Dim translations As New Dictionary(Of String, String)
translations.Add("yes", "yes")
translations.Add("ok", "yes")
translations.Add("yep", "yes")
translations.Add("yeah", "yes")
translations.Add("no", "no")
translations.Add("nope", "no")
With these definitions you can get the standard version of an answer very easily
Dim userInput = "yeah"
Dim response As String = Nothing
translations.TryGetValue(userInput, response)
Select Case response
Case "yes"
Console.WriteLine("ok")
Case "no"
Console.WriteLine("cancel")
Case Else
Console.WriteLine("rubbish!")
End Select
Dictionaries have the fastest lookup times.

Resources