Display line says index was out of range - vb.net

There is a button that displays an item from an array by searching an input in another array. In this instance, I want to display an item from animalArray.
To do this, a user needs to input a certain name from the nameArray. Binary search, will compare the input and nameArray(middle). But when all that is done, when displaying the array using listbox it crashes and says "index was out of bounds of the array". Even though all items in all arrays has 9 items including 0.
binarySearch(nameArray, animalArray, InputBox("Enter Owner name", "Owner name"))
How to solve this?
Sub binarySearch(ByVal array1, ByVal array2, ByVal item)
Sort()
Dim low = 0
Dim high = 9
Dim middle As String
Dim subfindindex = -1
Do While low <= high And subfindindex = -1
middle = (low + high) \ 2
If array1(middle) = item Then
subfindindex = middle
Exit Do
End If
If middle > item Then
high = middle - 1
Else
low = middle + 1
End If
Loop
ListBox1.Items.Add(array2(subfindindex)) '<--- index was out of bounds of array
End Sub

Your main problem is that you're comparing a number to a name, and attempting to determine which is greater:
If middle > item Then
Instead, you'll want to compare the number (the value of the middle variable) to the index of the name in the name array.
That said, you'll get more mileage with this by using lists instead of arrays, and by descriptively naming and strongly typing your parameter variables. Here's the working code:
Public Class Form1
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim Animals As New List(Of String)
Dim Names As New List(Of String)
Animals.Add("Dog")
Animals.Add("Cat")
Animals.Add("Sheep")
Animals.Add("Goat")
Animals.Add("Chicken")
Animals.Add("Fish")
Animals.Add("Cow")
Animals.Add("Snake")
Animals.Add("Rabbit")
Animals.Add("Squirrel")
Names.Add("Jim")
Names.Add("Betty")
Names.Add("Frank")
Names.Add("Paul")
Names.Add("Susan")
Names.Add("Debbie")
Names.Add("Bob")
Names.Add("Tina")
Names.Add("Fred")
Names.Add("Bill")
Me.BinarySearch(Names, Animals, InputBox("Enter Owner name", "Owner name"))
End Sub
Sub BinarySearch(Names As List(Of String), Animals As List(Of String), Name As String)
Dim SubFindIndex As Integer
Dim Middle As Integer
Dim High As Integer
Dim Low As Integer
SubFindIndex = -1
High = 9
Low = 0
Do While Low <= High And SubFindIndex = -1
Middle = (Low + High) \ 2
If Names(Middle) = Name Then
SubFindIndex = Middle
Exit Do
End If
If Middle > Names.IndexOf(Name) Then
High = Middle - 1
Else
Low = Middle + 1
End If
Loop
Me.ListBox1.Items.Add(Animals(SubFindIndex))
End Sub
End Class
But there's a lot simpler way of accomplishing the same goal:
Sub BinarySearch2(Names As List(Of String), Animals As List(Of String), Name As String)
Dim Animal As String
Dim Index As Integer
Index = Names.IndexOf(Name)
Animal = Animals(Index)
Me.ListBox1.Items.Add(Animal)
End Sub
--EDIT--
Here's what you should see after entering a name from the list in the InputBox:

Related

Fill in missing numbers in different lists twice. ArgumentOutOfRangeException

I need your help to prepare data. I am reading a byte array. I make bytes to unsigned integers. I read in different blocks of that array and write the UInt32s in 5 lists in total. The data has been stored compressed; that is, some spaces are missing and I need to fill them up. To make it clear, I made a compilable test project for you and wrote the data into an excel file.
This is the original data. From the left to the right: Sizes, Addresses, Indexes, Number_of_items, Description
You can see that in column C the 2, 3, and 4 are missing. So I select columns C through E, and move them down 3 rows. I fill the gaps with 2, 3, 4 in column C and 1, 1, 1 in the other two columns.
I do this until I reach the end of column B. Columns B, C, D, and E must have the same length.
Where I have a little problem
I fail because a While or For loop evaluates the List.Count property only once. That is, if I add something to a list within the loop, the loop doesn't run often enough. I've provisionally worked around this by writing While True and catching an OutOfRangeException. Maybe someone has a better idea; or even an idea that completely replaces my approach :D
Step № 2
If a row has a 2 in column D, I select columns B through E below the 2, and move the contents down one row (only one, because the difference is 1).
I want to do this until I get to the bottom of the table. This will make all columns the same length.
Again, I have the problem that I use While True and go out using an exception. Does anyone have a better idea?
FormMain.vb
Public NotInheritable Class FormMain
Private Sizes As New List(Of UInt32) From {
58_355UI,
20_270UI,
4_830UI,
4_443UI,
25_177UI,
8_844UI,
4_101UI,
4_200UI,
14_991UI,
12_639UI,
12_894UI,
14_165UI,
12_954UI,
26_670UI,
7_388UI}
Private Addresses As New List(Of UInt32) From {4_323UI, 62_706UI, 83_646UI, 88_935UI, 93_883UI, 128_259UI, 132_718UI,
137_254UI, 152_590UI, 178_485UI, 193_022UI, 206_718UI}
Private Indexes As New List(Of UInt32) From {1UI, 5UI, 6UI, 9UI, 10UI, 12UI}
Private NumberOfItems As New List(Of UInt32) From {1UI, 2UI, 1UI, 2UI, 1UI, 2UI}
Private Description As New List(Of UInt32) From {1UI, 1UI, 1UI, 1UI, 1UI, 1UI}
Private Sub ButtonStart_Click(sender As Object, e As EventArgs) Handles ButtonStart.Click
Dim RopD As New Reprocessing_of_parsed_data(Sizes, Addresses, Indexes, NumberOfItems, Description)
RopD.Fill_gaps()
End Sub
End Class
Reprocessing_of_parsed_data.vb
Public NotInheritable Class Reprocessing_of_parsed_data
Public Property Sizes As New List(Of UInteger)
Public Property Addresses As New List(Of UInteger)
Public Property Indexes As New List(Of UInteger)
Public Property Number_of_items As New List(Of UInteger)
Public Property Description As New List(Of UInteger)
Public Sub New(sizes As List(Of UInt32), addresses As List(Of UInt32), indexes As List(Of UInt32), number_of_items As List(Of UInt32), description As List(Of UInt32))
Me.Sizes = sizes
Me.Addresses = addresses
Me.Indexes = indexes
Me.Number_of_items = number_of_items
Me.Description = description
End Sub
Public Sub Fill_gaps()
Dim counterForAddressesList As Integer = 0
'Dim ListCount As Integer = Indexes.Count - 2
Dim i As Integer = 0
While True 'i < ListCount - 2
Try
Dim delta As Integer = CInt(Indexes(i + 1) - Indexes(i)) - 1
Dim number As UInt32 = Indexes(i)
While delta > 0
number += 1UI
counterForAddressesList += 1
Indexes.Insert(CInt(number) - 1, number)
Number_of_items.Insert(CInt(number) - 1, 1UI)
Description.Insert(CInt(number) - 1, 1UI)
delta -= 1
'ListCount += 1
End While
counterForAddressesList += 1
i += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
' Step 2
Dim j As Integer = 0
While True
Try
If Number_of_items(j) > 1UI Then
Dim delta As Integer = CInt(Number_of_items(j)) - 1
While delta > 0
Addresses.Insert(j + 1, UInteger.MaxValue)
Indexes.Insert(j + 1, UInteger.MaxValue)
Number_of_items.Insert(j + 1, UInteger.MaxValue)
Description.Insert(j + 1, UInteger.MaxValue)
delta -= 1
j += 1
End While
End If
j += 1
Catch ex As ArgumentOutOfRangeException
Exit While
End Try
End While
End Sub
End Class
It is never a good idea to catch an index out of bounds exception in a Try-Catch-statement. Only conditions you are not in control of (often I/O errors) should be handled at runtime. An index being out of bounds is a design error and must be fixed at design time.
I extracted the two steps from Sub Fill_gaps into two new methods to make the code easier to read and test.
Public Sub Fill_gaps() ' A better name would be "Decompress"
PrintTable() 'For testing
FillGaps()
PrintTable() 'For testing
AddMissingNumberOfItems()
PrintTable() 'For testing
End Sub
I also added a method PrintTable for testing
Private Sub PrintTable()
Console.WriteLine()
Console.WriteLine($" A B C D E")
For i = 0 To Sizes.Count - 1
Dim A = Sizes(i)
Dim B = If(i < Addresses.Count, Addresses(i), 0UI)
Dim C = If(i < Indexes.Count, Indexes(i), 0UI)
Dim D = If(i < NumberOfItems.Count, NumberOfItems(i), 0UI)
Dim E = If(i < Description.Count, Description(i), 0UI)
Console.WriteLine($"{A,10}{B,10}{C,10}{D,10}{E,10}")
Next
End Sub
Step 1: fill the gaps (the method is self-explanatory):
Private Sub FillGaps()
' Fill gaps in columns C, D and E.
' The number of Addresses B indicates the total number of indexes.
' Append empty items to C, D and E until the list counts matches the
' expected total number of indexes.
Dim originalIndexCount = Indexes.Count 'Save original count
Do While Indexes.Count < Addresses.Count
Indexes.Add(CUInt(Indexes.Count + 1)) ' Make index 1-based
NumberOfItems.Add(1)
Description.Add(1)
Loop
'Move the rows to where the index indicates.
'We do it backwards to not overwrite existing items.
For i As Integer = originalIndexCount - 1 To 0 Step -1
Dim targetIndex = CInt(Indexes(i)) - 1 ' Subtract 1, indexes are 0-based
If targetIndex <> i Then
' Copy to target position
Indexes(targetIndex) = Indexes(i)
NumberOfItems(targetIndex) = NumberOfItems(i)
Description(targetIndex) = Description(i)
'Clear resp. initialize old row
Indexes(i) = CUInt(i + 1) ' Make index 1-based
NumberOfItems(i) = 1
Description(i) = 1
End If
Next
End Sub
Step 2:
Private Sub AddMissingNumberOfItems()
' Insert empty rows after items with NumberOfItems > 1.
' We do it backwards to not mess up our indexes.
For i As Integer = Indexes.Count - 1 To 0 Step -1
For k As UInteger = 2 To NumberOfItems(i)
Addresses.Insert(i + 1, 0)
Indexes.Insert(i + 1, 0)
NumberOfItems.Insert(i + 1, 0)
Description.Insert(i + 1, 0)
Next
Next
End Sub
If you use the following test list for the descriptions, you will better see which rows have been moved or added
Private Description As New List(Of UInt32) From {2UI, 3UI, 4UI, 5UI, 6UI, 7UI}

create 1000, 8-character random ID's, all unique and display in a text box

enter image description hereWrite a program to display 1000 8-character random user IDs in a text box after you click a button. Make sure the program verifies that none of the IDs are identical. Each userid should include a mixture of alphabetic characters and numbers.
What I have so far, which could be enough to get by as a random ID generator alone, but it does not loop 1000 times to be displayed in the label, and doesn't check for repeated ID's.
Public Class Form1
Private Sub btnGenerateRandomID_Click(sender As Object, e As EventArgs) Handles btnGenerateRandomID.Click
Dim rand As New Random
Dim char1 As String
Dim char2 As String
Dim char3 As String
Dim char4 As String
Dim char5 As String
Dim char6 As String
Dim char7 As String
Dim char8 As String
char1 = ChrW(rand.Next(Asc("A"), Asc("Z") + 1))
char2 = ChrW(rand.Next(Asc("A"), Asc("Z") + 1))
char3 = ChrW(rand.Next(Asc("A"), Asc("Z") + 1))
char4 = ChrW(rand.Next(Asc("A"), Asc("Z") + 1))
char5 = rand.Next(0, 9)
char6 = rand.Next(0, 9)
char7 = rand.Next(0, 9)
char8 = rand.Next(0, 9)
lblRandomId.Text = char1 + char2 + char3 + char4 + char5 + char6 + char7 + char8
End Sub
End Class
Thanks.
EDIT:
Public Class Form1
'Write a program to display 1000 8-character random user IDs in a text
'box after you click a button. Make sure the program verifies that none Of the IDs are identical.
'Each userid should include a mixture Of alphabetic characters And numbers.
Private Sub btnGenerateRandomID_Click(sender As Object, e As EventArgs) Handles btnGenerateRandomID.Click
Dim strChar As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
Dim rand As New Random
Dim strID As String = ""
For count_ids As Integer = 0 To 999
For count_chars As Integer = 0 To 7
strID += strChar(rand.Next(0, 62))
Next count_chars
lblRandomId.Text = strID
Next
End Sub
End Class
Here is the actual question "Q. Write a program to display 1000 8-character random user IDs in a text box after you click a button. Make sure the program verifies that none of the IDs are identical. Each userid should include a mixture of alphabetic characters and numbers.
Explain your program in your essay, with screenshots. Include a paragraph on the random number generator used in Visual Basic, and answer the question: how should the random number generator be best seeded?"
First of all, Welcome to StackOverflow,
From what i understand from your post you want to generate 1000 8 Character long Unique ID's
First of all we declare our variables
Dim Characters As String = "AZERTYUIOPQSDFGHJKLMWXCVBN0123456789" 'Declare All Characters on one string
Dim IDsList As New List(Of String) With {.Capacity = 1000} 'You can limit the capacity to 1000
Dim Id As String = ""
Dim Rand As New Random
Now we begin to genearte ID's and add them to IDsList using a nested loop
For i As Integer = 0 To 999 'This is a Zero Based Index so 999 is actually 1000
For _i As Integer = 0 To 7 'This also means 7 is 8
Id += Characters(Rand.Next(0, 36))
Next
IDsList.Add(Id)
Id = ""
Next
'You can Check for duplicates by adding this piece of code or use the next one instead
Dim NoDupesIDsList As New List(Of String)
NoDupesIDsList = IDsList.Distinct.ToList
For i As Integer = 0 To 999 'This is a Zero Based Index so 999 is actually 1000
For _i As Integer = 0 To 7 'This also means 7 is 8
Id += Characters(Rand.Next(0, 36))
Next
If not IDsList.contains(Id) then
IDsList.Add(Id)
End if
Id = ""
Next
Use a Textbox with multiline and scroll bar for better experience
Feel free to ask me anything via comments
PS: I tested my method and it's working , enjoy it !
This function will return the ASCII characters between 48 an 122. That will include some characters that are not letters or numbers but this makes the ids even more unique and simplifies the code.
The For loop builds the ids with a length of 8. Then the id is checked for uniqueness with the .Contains method. Only if it is not in the list, it is added to the list and the counter is incremented.
Private Rnd As New Random
Private Function GetListOfIDs() As List(Of String)
Dim Counter As Integer
Dim lst As New List(Of String)
Dim id As String = ""
Do While Counter < 1000
id = ""
For i = 0 To 7
id &= Chr(Rnd.Next(48, 123))
Next
If Not lst.Contains(id) Then
lst.Add(id)
Counter += 1
End If
Loop
Return lst
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim lst = GetListOfIDs()
TextBox1.Text = String.Join(vbCrLf, lst)
End Sub
EDIT
As per suggestion by Caius Jard in comments I have added a solution using a HashSet. Same idea only a HashSet will not accept duplicate entries. The .Add method returns a boolean telling you if the Add was successful.
Private Function GetListOfIDs() As HashSet(Of String)
Dim hs As New HashSet(Of String)
Dim id As String = ""
Do While hs.Count < 1001
id = ""
For i = 0 To 7
id &= Chr(Rnd.Next(48, 123))
Next
If hs.Add(id) Then
Counter += 1
End If
Loop
Return hs
End Function
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim hs = BuildHashSet()
TextBox1.Text = String.Join(vbCrLf, hs)
End Sub
This method is probably more efficient because a HashSet is designed for hight performance. For 1000 items I did not notice any difference in the user interface. The text box updated immediately with both methods.
This will create the names:
Function RandomName(length As Integer) As String
Shared rnd As New Random()
Shared corpus() As Char = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789".ToCharArray()
Return New String(Enumerable.Range(0, length).Select(Function(c) corpus(rnd.Next(corpus.Length))).ToArray())
End Function
Iterator Function GetUniqueNames(length As Integer, count As Integer) As IEnumerable(Of String)
Dim names As New HashSet(Of String)
For i As Integer = 0 To count
Dim name As String = RandomName(length)
While names.Contains(name)
'Potential (but unlikely) halting problem
name = RandomName(length)
End While
names.Add(name)
Yield name
Next
End Function
Dim uniqueNames = GetUniqueNames(8, 1000)
But do you really want to display 1000 of them in a single label?
lblRandomId.Text = string.Join(vbCrLf, uniqueNames)
Let's talk about your code.
Dim strChar As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
This assignment will work in your loop because a String is really an array of Char. This is why you can refer to an index in the string.
Dim rand As New Random()
The .net Random class provides a seed for Random based on system time. Only rarely would you want to provide a seed.
From the docs https://learn.microsoft.com/en-us/dotnet/api/system.random?view=netcore-3.1
CONSTRUCTORS
Random() Initializes a new instance of the Random class
using a default seed value.
Random(Int32) Initializes a new instance of the Random class, using
the specified seed value.
For count_ids As Integer = 0 To 999
A For loop is not appropriate for this problem. We only want to increment the counter if we get a unique ID. The above For loop will increment on every iteration.
Do While Counter < 1000
'Code here
Loop
Notice in my code the counter is only incremented if the ID is unique.
The following loop should work fine but above this code we need to reset strID to an empty string. Otherwise we will have a very long string.
For count_chars As Integer = 0 To 7
strID += strChar(rand.Next(0, 62))
Next count_chars
You should not update the user interface (the label) on each iteration of the loop. Repainting the screen is one of the slowest operations in code. This line will overwrite the string in the label on every iteration.
lblRandomId.Text = strID
Move the update of the UI to after both loops are complete
Your code never checks for duplicates.
Now let's fix it.
We use a List(Of T) to accumulate the IDs. The T stands for Type; in our case it is String. A List is like an array only you don't have to know ahead of time how many entries there will be. No ReDim Preserve required.
Moved Dim strID As String = "" to the beginning of the inner loop so we will get a new strID on each iteration of the outer loop.
Changed strID &= strChar(rand.Next(0, 62)). &= instead of +=. In VB.net we use the ampersand to concatenate strings although the plus sign will work. The .Next method of the Random class has an overload that takes 2 Integers. It is inclusive of the first and exclusive of the second which means (0, 62) will return numbers form 0 to 61. Your string with a length of 62 has indexes of 0 to 61.
Next we check for duplicates with the .Contains method of the List class. This method does just what you would expect. We pass the new ID to the method and it returns a Boolean telling if the string is already in the list.
If it is not in the list, we add it to the list and increment the counter. The counter is only incremented if we have a successful add.
Finally we update the user interface by using a method of the String class. the .Join method takes a separator and the list to join. The TextBox must be multiline and have scroll bars.
I changed the names of the controls so I could test it in my testing program.
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
Dim lst As New List(Of String)
Dim strChar As String = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
Dim rand As New Random()
Dim counter As Integer
Do While counter < 1000
Dim strID As String = ""
For count_chars = 0 To 7
strID &= strChar(rand.Next(0, 62))
Next
If Not lst.Contains(strID) Then
lst.Add(strID)
counter += 1
End If
Loop
TextBox1.Text = String.Join(vbCrLf, lst)
End Sub
EDIT
The official documentation for this method can be found here.
https://learn.microsoft.com/en-us/dotnet/api/system.string.join?view=netcore-3.1
I know it will be difficult to read but just take a look. Eventually you will be able to understand it and will be using these docs a lot.
String.Join(separator, List(Of String))
The separator is what to put between the strings in the list. In our case that is vbCrLf which is a vb constant for new line. Cr = Carraiage Return and Lf = Line Feed. Other types of collections that contain strings can also use this method.
The method takes each string in the list and adds the separator. After it is done with the list it returns one long string which we display in the TextBox.

Visual Basic (.NET) > randomize listbox picks (unique)

I just want to make randomizer program. It will be used to pick tournament pairs (like f.e. UEFA Champions League quaterfinal pairs)
GUI Screen: http://i.imgur.com/jqBMJjt.png
I have trouble with this.. When I tried to make picker, it works well > from listbox 1 (left side) to listbox2 (50% of listbox1 items), listbox3 (50% of listbox1 items).. (left side) but these picks aren't unique. There are some duplicates as you can see on image (2nd listbox 2x noob).
Part of my code:
Private Sub RandomiseListBox()
Dim count As Integer = CarbonFiberListBox1.Items.Count
Dim countt As Integer
'countt = count / 2
Dim item As String
Dim itemz As New List(Of String)()
Dim repeat As New List(Of String)()
Dim aa, bb As Integer
If Not count = 0 And ((count Mod 2) = 0) Then
CarbonFiberListBox2.Items.Clear()
CarbonFiberListBox3.Items.Clear()
For index As Integer = 0 To countt - 1 Step 1
item = Me.CarbonFiberListBox1.Items(Me.randomiser.Next(index, count))
itemz.Add(item)
'Me.CarbonFiberListBox1.Items.Remove(item)
'Me.CarbonFiberListBox1.Items.Insert(index, item)
Me.CarbonFiberListBox2.Items.Insert(index, item)
Next index
For index As Integer = 0 To countt - 1 Step 1
For aa = 0 To bb = 999
item = Me.CarbonFiberListBox1.Items(Me.randomiser.Next(index, count))
If Not (itemz.Contains(item)) And Not (repeat.Contains(item)) Then
repeat.Add(item)
'Me.CarbonFiberListBox1.Items.Remove(item)
'Me.CarbonFiberListBox1.Items.Insert(index, item)
Me.CarbonFiberListBox3.Items.Insert(index, item)
End If
Next
Next index
'For index As Integer = 0 To countt - 1 Step 1
'Next index
ElseIf count > 0 Then
'CarbonFiberButton4.Text = "ODD PARTICIPANTS!"
Else
End If
End Sub
Can I get help? I think it is so easy.
Maybe this little example will give you an idea. It works by getting a list, in random order, from one listbox. Note how the list is depleted as items are added to the other listboxes which stops duplicates.
Private Sub Button2_Click(sender As Object, e As EventArgs) Handles Button2.Click
'some sample data
ListBox1.DataSource = {"one", "two", "three", "four", "five", "six", "seven"}
DoRandom()
End Sub
Private Shared prng As New Random
Private Sub DoRandom()
ListBox2.Items.Clear()
ListBox3.Items.Clear()
Dim l As New List(Of String)
'random order of items in ListBox1
l.AddRange(ListBox1.Items.Cast(Of String).OrderBy(Function(s) prng.Next))
'add half to lb2
For x As Integer = 0 To l.Count \ 2
ListBox2.Items.Add(l(0))
l.RemoveAt(0)
Next
'remainder to lb3
For x As Integer = 0 To l.Count - 1
ListBox3.Items.Add(l(0))
l.RemoveAt(0)
Next
End Sub

how do i change the colour of a button based on a string input

For my computing project I am creating a game of risk this requires me to randomly generate the initial countries of the players (I have started with 2 players for simplicity), for this I am going to have the buttons (which represent the countries) to change colour to denote which of the players country it is.
I have tried to assign each player a string value of their colour then set the button back colour to that value. The problem with that at the moment is with the line of "b.Tag.BackColor = Players_Colours(N)" as the value that Players_Colours(N) gives is "S"c for some reason.
Any ideas why that is the case or anything would be appreciated.
Public Sub New()
' This call is required by the designer.
InitializeComponent()
' Add any initialization after the InitializeComponent() call.
End Sub
Public Function Random_country_picker(ByVal players)
Dim County_List = {"Oxfordshire", "Buckinhamshire", "Berkshire", "London", "Hampshire", "Surrey", "West_Sussex", "East_Sussex", "Kent", "Bedfordshire", "Cambridgeshire", "Hertfordshire", "Essex", "Cornwall", "Devon", "Gloucestershire", "Whiltshire", "Lancashire", "Cumbria", "Cheshire", "Manchester", "Merseyside", "Lincolnshire", "Staffordshire", "Herefordshire", "County_Durham", "North_Yorkshire", "South_Yorkshire", "Warwickshire", "Northumberland", "West_Yorkshire", "East_Yorkshire", "Shropshire", "Northamptonshire", "Derbyshire", "Nottinghamshire", "Worcestershire", "Somerset", "Dorset", "Isle_Of_White", "Norfolk", "Suffolk"}
Dim value As String
Dim num = 0
Dim nump = 42 / players
Dim N As Integer
Dim Players_Colours As String
Dim z As String
Players_Colours = {"Pink", "Red"}.ToString
N = 0
Randomize()
Dim rnd As New Random()
Dim index As Integer
While nump > num
If N > Players_Colours.Length Then
N = 0
End If
index = rnd.Next(0, County_List.Length)
value = County_List(index)
For Each b As Windows.Forms.Button In Me.Controls
If b.Tag = index Then
b.Tag.BackColor = Players_Colours(N)
End If
Next
Array.Clear(County_List, index, 1)
num += 1
N += 1
End While
Return 0
End Function
Random_country_picker(2)

What is the best way to calculate word frequency in VB.NET?

There are some good examples on how to calculate word frequencies in C#, but none of them are comprehensive and I really need one in VB.NET.
My current approach is limited to one word per frequency count. What is the best way to change this so that I can get a completely accurate word frequency listing?
wordFreq = New Hashtable()
Dim words As String() = Regex.Split(inputText, "(\W)")
For i As Integer = 0 To words.Length - 1
If words(i) <> "" Then
Dim realWord As Boolean = True
For j As Integer = 0 To words(i).Length - 1
If Char.IsLetter(words(i).Chars(j)) = False Then
realWord = False
End If
Next j
If realWord = True Then
If wordFreq.Contains(words(i).ToLower()) Then
wordFreq(words(i).ToLower()) += 1
Else
wordFreq.Add(words(i).ToLower, 1)
End If
End If
End If
Next
Me.wordCount = New SortedList
For Each de As DictionaryEntry In wordFreq
If wordCount.ContainsKey(de.Value) = False Then
wordCount.Add(de.Value, de.Key)
End If
Next
I'd prefer an actual code snippet, but generic 'oh yeah...use this and run that' would work as well.
This might be what your looking for:
Dim Words = "Hello World ))))) This is a test Hello World"
Dim CountTheWords = From str In Words.Split(" ") _
Where Char.IsLetter(str) _
Group By str Into Count()
I have just tested it and it does work
EDIT! I have added code to make sure that it counts only letters and not symbols.
FYI: I found an article on how to use LINQ and target 2.0, its a feels bit dirty but it might help someone http://weblogs.asp.net/fmarguerie/archive/2007/09/05/linq-support-on-net-2-0.aspx
Public Class CountWords
Public Function WordCount(ByVal str As String) As Dictionary(Of String, Integer)
Dim ret As Dictionary(Of String, Integer) = New Dictionary(Of String, Integer)
Dim word As String = ""
Dim add As Boolean = True
Dim ch As Char
str = str.ToLower
For index As Integer = 1 To str.Length - 1 Step index + 1
ch = str(index)
If Char.IsLetter(ch) Then
add = True
word += ch
ElseIf add And word.Length Then
If Not ret.ContainsKey(word) Then
ret(word) = 1
Else
ret(word) += 1
End If
word = ""
End If
Next
Return ret
End Function
End Class
Then for a quick demo application, create a winforms app with one multiline textbox called InputBox, one listview called OutputList and one button called CountBtn. In the list view create two columns - "Word" and "Freq." Select the "details" list type. Add an event handler for CountBtn. Then use this code:
Imports System.Windows.Forms.ListViewItem
Public Class MainForm
Private WordCounts As CountWords = New CountWords
Private Sub CountBtn_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles CountBtn.Click
OutputList.Items.Clear()
Dim ret As Dictionary(Of String, Integer) = Me.WordCounts.WordCount(InputBox.Text)
For Each item As String In ret.Keys
Dim litem As ListViewItem = New ListViewItem
litem.Text = item
Dim csitem As ListViewSubItem = New ListViewSubItem(litem, ret.Item(item).ToString())
litem.SubItems.Add(csitem)
OutputList.Items.Add(litem)
Word.Width = -1
Freq.Width = -1
Next
End Sub
End Class
You did a terrible terrible thing to make me write this in VB and I will never forgive you.
:p
Good luck!
EDIT
Fixed blank string bug and case bug
This might be helpful:
Word frequency algorithm for natural language processing
Pretty close, but \w+ is a good regex to match with (matches word characters only).
Public Function CountWords(ByVal inputText as String) As Dictionary(Of String, Integer)
Dim frequency As New Dictionary(Of String, Integer)
For Each wordMatch as Match in Regex.Match(inputText, "\w+")
If frequency.ContainsKey(wordMatch.Value.ToLower()) Then
frequency(wordMatch.Value.ToLower()) += 1
Else
frequency.Add(wordMatch.Value.ToLower(), 1)
End If
Next
Return frequency
End Function