Visual Studio (List.Count - 1) = List.Count - vb.net

I've been trying around for a bit now and I'm totally confused why my program behaves this way.
I tried to search on google and here but maybe I just have trouble phrasing my question right or I'm the only one with this problem.
Anyways, so here's my problem:
Protected myList as List(Of CustomClass) = new List(Of CustomClass)
Public Sub mySub()
Dim ListCount as Integer = myList.Count
ListCount = ListCount - 1
For i As Integer = 0 To ListCount Step 1
If myList(i).MyStatus = FMyStatus Then
myList(i).MyFunction.Invoke()
myList.RemoveAt(i)
End If
Next
End Sub
This throws me an exception all the time that i is larger than the myList-index.
The problem is that ListCount even though I reduced it by 1 still stays the same, it's not possible to reduce it by 1.
But if I for example reduce it by 2, it's totally fine and does it stuff.
Is this common? Did I miss on something? I already tried to find something on the msdn website for the List.Count Property but wasn't able to find something that explains me why I can't reduce the value by 1.
Hope you guys can enlighten me.
Cheers.

You need to loop backwards to accomplish this:
For i As Integer = myList.Count - 1 To 0 Step -1
If you go the other way, when you remove an item, the list becomes smaller, but you are still trying to go to the end of the original list.

Related

DataGridView Remove Error

I want to delete the duplicate value and blank lines in the DataGridView.
I am using the following code for this.
Private Sub dgwsil()
On Error Resume Next
For i2 As Integer = DataGridView1.RowCount - 1 To 0 Step -1
If Trim(DataGridView1.Rows(i2).Cells(0).Value.ToString()) = "" Then
DataGridView1.Rows.RemoveAt(i2)
End If
Next
Dim numberOfRows = DataGridView1.Rows.Count
Dim i As Integer = 0
While i < numberOfRows - 2
For ii As Integer = (numberOfRows - 1) To (i + 1) Step -1
If DataGridView1.Rows(i).Cells(0).Value.ToString() = DataGridView1.Rows(ii).Cells(0).Value.ToString() Then
DataGridView1.Rows.Remove(DataGridView1.Rows(ii))
numberOfRows -= 1
End If
Next
i += 1
End While
End Sub
Code sometimes works fine.
But sometimes it gives an error
How can I solve this problem ? Or is there any code you use for it?
It looks like you are going into this problem with a VBA mindset. In VBA, you would usually manipulate the datagridview directly and the On Error Resume Next line would be acceptable.
In VB.NET you don't want to do either of those things. Your datagridview should be bound to a collection of information. That collection can be a datatable or an array or any type of collection really. So instead of using a loop to manipulate the datagridview directly you would either loop through the backing collection or better yet LINQ through the collection and manipulate it however you need. There are a ton of tutorials out there explaining everything you could ever need so googling around for how to bind a datatable to a datagridview should get you what you need.
I would also suggest looking into the Try...Catch...Finally... structure. You should really use that to handle any errors that could possibly come up from your code.

Vb Guessing Game Random generator broken

In my code,I have now realised I have to use the New Random function, my code was working before with the Randomize and then the numbers but now it comes up with loads of errors and wont even let me run the program. I think it is only a small error but I just need some help to get the final bit going
Heres the code and thanks for any help :)
I cannot get the code to work with the randomly generated number and I have to use the New Random function I cannot use randomize() Does anybody know how to help here is the code.
Dim timestook As Int32 = 1
Dim usersguess As Integer
Dim value = New Random(0 - 19)
Console.WriteLine("You have to guess this number. It is between 1 and 20. Good Luck !")
usersguess = Console.ReadLine()
'keep looping until they get the right value
While usersguess <> value
'now check how it compares to the random value
If usersguess < value Then
timestook = timestook + 1
Console.WriteLine("You're too low. Go higher ")
ElseIf usersguess > value Then
Console.WriteLine("You're too high. Go Lower.")
timestook = timestook + 1
End If
'If they are wrong the code will run again,after telling the user if they are too high or too low.
usersguess = Console.ReadLine()
End While
' Console.WriteLine("You're correct. Well Done")
If usersguess = value Then
Console.WriteLine("You took,{0}", timestook)
End If
Console.ReadLine()
End Sub
You'll want to do some googling on how to use random numbers. Your problem is that you aren't creating a Random object to handle the random number generation.
Here's how you can fix your code:
Dim randNumGen As New Random() 'Create Random object
Dim value As Integer = randNumGen.Next(0, 20) 'set value equal to a new random number between 0-19
Please note that this code could be further refactored for readability and simplicity (like changing timestook = timestook + 1 to timestook += 1 and selecting better variable names like numberOfGuesses as opposed to timestook, etc.
The expression New Random(0-19) does not do at all what you think it does, name it does NOT return an integer. Instead, it creates an instance of a Random object, which is a type that knows how to create new random values. The 0-19 part of the expression is the seed for the Random object's constructor, and is the same as just passing the value -19.
This looks like it's either homework or personal practice, so I feel like you will be better served in this case with a separate example using the Random type for reference than you would if I fixed the code sample in the question for you:
Dim rnd As New Random()
For i As Integer = 0 To 10
Console.WriteLine(rnd.Next(0, 20))
Next i
It's also worth mentioning here that you typically only want one Random object for your entire program, or at least only one Random object for each logical part of your program. Creating new Random objects resets the seeds, and for best results you want to follow the same seed on subsequent calls to the same instance for a while.

VB.NET Is there a quicker way to scan through a RichTextBox?

I have a VB.NET application that I use to load various files into a RichTextBox and then scan through the document to find specific words. It's similar to the Find function in Word. The app was running fine until a 5,150 line .sql document run through it and it's taking upwards of 10 minutes to run to completion.
Can anyone recommend a better way of coding it than I have below?
If sqlText.Contains("GRANT") Then
Dim searchstring As String = "GRANT"
Dim count As New List(Of Integer)()
For i As Integer = 0 To rtbFile.Text.Length - 1
If rtbFile.Text.IndexOf(searchstring, i) <> -1 Then
count.Add(rtbFile.Text.IndexOf(searchstring, i))
End If
Next
Try
For i As Integer = 0 To count.Count - 1
rtbFile.Select(count(i), searchstring.Length)
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
count.RemoveAt(i)
Next
Catch ex As Exception
End Try
rtbFile.Select(rtbFile.Text.Length, 0)
rtbFile.SelectionBackColor = Color.White
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Regular)
End If
That first loop is killing the performance, you are calling IndexOf for every character in the string. Also the two loops can be merged in to one. Change it to:
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
For Each m As Match in Regex.Matches(sertbFile.Text, searchstring)
rtbFile.Select(m.Index, searchstring.Length)
Next
This can also be done with a While loop and RichTextBox.Find():
Dim searchstring As String = "GRANT"
Dim index As Integer = rtbFile.Find(searchstring, 0, RichTextBoxFinds.None)
While index <> -1
rtbFile.Select(index, searchstring.Length)
rtbFile.SelectionBackColor = Color.Yellow
rtbFile.SelectionFont = New Font(rtbFile.Font, FontStyle.Bold)
index = rtbFile.Find(searchstring, index + searchstring.Length, RichTextBoxFinds.None)
End While
You've got a few bad things going on here:
First, the following code:
For i As Integer = 0 To rtbFile.Text.Length - 1
If rtbFile.Text.IndexOf(searchstring, i) <> -1 Then
count.Add(rtbFile.Text.IndexOf(searchstring, i))
End If
Next
This is looping through every character in your string, and calling IndexOf on the entire string from that point forward. So your 50,000-character string is running IndexOf 50,000 times, on large strings.
You only need to call IndexOf as many times as you find a string. When your string is found, you increment your start index to that point, and keep searching only from that point.
Next thing, this code:
For i As Integer = 0 To count.Count - 1
...
count.RemoveAt(i)
Next
The RemoveAt line is unnecessary. You're already looping through a list, so you don't need to remove the items as you go along. The way it stands, your loop will skip every other item in your list.
Whoops. I missed a very important point about the IndexOf (and incorrectly assumed it was fed with the end of the last match). See Magnus's answer.
I am not sure where the bottleneck is (and it might very well be from setting the selection itself), but here are my suggestions, roughly in order of priority:
Invoke rtbFile.Text once to avoid any roundtrips to underlying control (perhaps a native Windows control?) and use a variable to store the resulting string. Once the string is obtained in .NET, just keep using it directly unless/until the text may change. If the control is native then a lot of work may be required to simply "get the text".
Use normal item iteration over the count collection (not indexing) and do not remove from the front-of the List when assigning the selections. Removing from the front of a List is "expensive" in that it must shift all items down internally. Also, removing the element is unneeded here and dubious at best: since the collection being modified is also is being iterated which likely leads to incorrect behavior (skipped items), regardless of performance.
Only call IndexOf once per loop and use a variable to avoid a duplicate search. This likely won't have an overall impact, but it does avoid some "extra" work. IndexOf itself is fine and doesn't need to be replaced.
YMMV.

VB - How do you remove "empty" items from a generic list?

I have a VB.NET (2010) project that contains a generic list, and I'm trying to figure out how to remove any "empty" items from the list. When I say "empty", I mean any item that does not contain any actual characters (but it may contain any amount of whitespace, or no whitespace at all).
For example, let's say this is my list...
Dim MyList As New List(Of String)
MyList.Add("a")
MyList.Add("")
MyList.Add("b")
MyList.Add(" ")
MyList.Add("c")
MyList.Add(" ")
MyList.Add("d")
I need it so that if I did a count on that list, it would return 4 items, instead of 7. For example...
Dim ListCount As Integer = MyList.Count
MessageBox.Show(ListCount) ' Should show "4"
It would be nice if there was something like...
MyList.RemoveEmpty
Anyways... I've been searching Google for a solution to this for the past few hours, but haven't been able to turn up anything so far. So... any ideas?
BTW, I'm targeting the .NET 2.0 framework for this project.
Thanks in advance!
You can use List.RemoveAll
MyList.RemoveAll(Function(str) String.IsNullOrWhiteSpace(str))
If you don't use at least .NET 4, you can't use String.IsNullOrWhiteSpace. Then you can implement the method yourself:
Public Shared Function IsNullOrWhiteSpace(value As String) As Boolean
If value Is Nothing Then
Return True
End If
For i As Integer = 0 To value.Length - 1
If Not Char.IsWhiteSpace(value(i)) Then
Return False
End If
Next
Return True
End Function
Note that Char.IsWhiteSpace is there since 1.1.
The post marked as solution didn't works for me.
Try this:
MyList.AsEnumerable().Where(Function (x) Not String.IsNullOrEmpty(x)).ToList
This must returns you a list without empty values.

VB2005 Find/Search in a ListOf Structure

VB2005: I've been looking for several hours on a good example and i found some but unfortunately they are for VB2008+. Im currently working in VB2005 so it seems like this was harder to do in that release.
I have a class for a point
Public Class cPoint
Public Speed As Integer
Public Alt As Integer
Public Status As String = ""
Public Err As String = ""
End Class
I populate a list of points with MyPoints=List(of cPoint). Now all i need to do is find the first match with a supplied speed and alt. I tried
Dim p As cPoint = MyPoints.Find(Function(item As cPoint) item.Speed = 85)
But that doesn't work in VB2005 much less work with more than 1 filter. I just cant seem to find a good example that works in VB2005. I could iterate through the list but its kind of big and not very efficient. Any tips on how i can do this in VB2005?
~agp
VB.Net 2005 lacks lambda support hence that style of query won't work. The simplest version that will is to manually iterate with a For Each loop.
My p As cPoint = Nothing
For Each item in MyPoints
If item.Speed = 85 Then
p = item
Exit For
End If
Next