Sort an array of structures in .NET - vb.net

This is one of those times when only the hive mind can help - no amount of Google-fu can!
I have an array of structures:
Structure stCar
Dim Name As String
Dim MPH As Integer
Sub New(ByVal _Name As String, ByVal _MPH As Integer)
Name = _Name
MPH = _MPH
End Sub
End Structure
How do I sort the array on one variable / property of the structure?
Dim cars() as stCar = {new stCar("ford",10), new stCar("honda",50)}
cars.sort("MPH") // how do I do this?

Assuming that the structure has a property called MPH:
cars = cars.OrderBy(Function(c) c.MPH)
Note: the above code was auto-converted from the following c# code (in case it contains errors):
cars = cars.OrderBy(c => c.MPH);

The easiest way to perform the sort would be to use LINQ to Objects.
Dim q = From c In cars Order By c.MPH Select c

I don't know VB.NET, but you should be able to do this my implimenting IComparer. Take a look at this example
http://www.java2s.com/Code/VB/Data-Structure/UseIComparertosortbydifferentproperties.htm
Alternativly you can also use Linq

Another possibility, that doesn't use Linq but instead uses the .Net Array class' Sort method:
Module Module1
Structure stCar
Dim Name As String
Dim MPH As String
Sub New(ByVal _Name As String, ByVal _MPH As Integer)
Name = _Name
MPH = _MPH
End Sub
End Structure
Class CarCompareMph : Implements IComparer
Public Function Compare(ByVal x As Object, ByVal y As Object) As Integer Implements System.Collections.IComparer.Compare
Dim xCar As stCar = DirectCast(x, stCar)
Dim yCar As stCar = DirectCast(y, stCar)
Return New CaseInsensitiveComparer().Compare(xCar.MPH, yCar.MPH)
End Function
End Class
Sub Main()
Dim cars() As stCar = {New stCar("honda", 50), New stCar("ford", 10)}
Array.Sort(cars, New CarCompareMph)
For Each c As stCar In cars
Console.WriteLine("{0} - {1} MPH", c.Name, c.MPH)
Next
End Sub
End Module
I'm not sure if that's what you're looking for, but it's another approach.

A simple way that seems to be working for me in vb.net 2013 is as follows:
cars.Sort(Function(c1,c2) c1.MPH.CompareTo(c2.MPH))

In VB.Net of VS2015 there is another method of sorting:
cars.Sort(New Comparison(Of stCar)(Function(x, y) 'x and y are of type of stCar
If x.MPH > y.MPH Then
Return 1 ' Return Value>0 means x>y
End If
If x.MPH < y.MPH Then
Return -1 ' Return Value<0 means x<y
End If
If x.MPH = y.MPH Then
Return 0 ' Return Value=0 means x=y
End If
End Function))

Public Class Form1
Public Structure EstruturaPessoa
Dim nome As String
Dim idade As Integer
End Structure
Private Sub Form1_Load(sender As Object, e As System.EventArgs) Handles Me.Load
Dim nome(,) As String = {{"Milton Inácio Pozza", 30}, _
{"Araci Moraes", 34}, _
{"Marli Lipi Jesus", 21}, _
{"Gerson Guebur", 45}, _
{"Marli Ulths", 72}, _
{"Mauro Jesus Nadolni", 56}, _
{"Cristiano Kobashikawa Ferreira", 44}, _
{"Débora Nadolni", 90}, _
{"Samanta Gomes Guebur", 66}, _
{"Miguel Barbosa", 42}, _
{"Luis Jesus", 24} _
}
Dim Pessoa As EstruturaPessoa
Dim ListaPessoa = New List(Of EstruturaPessoa)
Dim i As Integer
'Load ListaPessoa
For i = 0 To (nome.Length / 2) - 1
Pessoa.nome = nome(i, 0)
Pessoa.idade = nome(i, 1)
ListaPessoa.Add(Pessoa)
Next i
'Fill the ListView1 with the ListaPessoa before sorting
For Each item_pessoa In ListaPessoa
With Me.ListView1
.Items.Add(item_pessoa.nome)
With .Items(.Items.Count - 1).SubItems
.Add(item_pessoa.idade)
End With
End With
Next
'Sort ListaPessoa
ListaPessoa.Sort(Function(c1, c2) c1.nome.CompareTo(c2.nome))
'Modifiy the 6th item of ListaPessoa
Pessoa = ListaPessoa(5)
Pessoa.nome += " ***" 'Acrescenta asteriscos ao nome
Pessoa.idade = 99 'Modifica a idade para 99
ListaPessoa(5) = Pessoa 'Atualiza o item na ListaPessoa
'Fill ListView2 with the ListaPessoa after sorting
For Each item_pessoa In ListaPessoa
With Me.ListView2
.Items.Add(item_pessoa.nome)
With .Items(.Items.Count - 1).SubItems
.Add(item_pessoa.idade)
End With
End With
Next
End Sub
End Class

Related

Removing duplicates in Text Box and adding the corresponding values

I have a VB form with three TextBoxes. Here's an example of what I'd like the program to achieve:
So, that's the form ... the program sorts a text file and gets names, goals, and positions. E.g.
Jordan 26 Center
James 10 Mid
Jordan 4 Center
Jack 6 Forward
James 10 Mid
When the update button is clicked, the program should realize that James and Jordan are written twice, remove one of them and add their goals, so it should output:
Jordan 30 Center
James 20 Mid
Jack 6 Forward
To do this I've had the data transferred into ListBoxes which makes it easier to remove duplicates, the data is then transferred back into a multi-line TextBox so it is editable. Here's my code so far. It either gives the wrong results or an index out of range error.
Dim Count1 As Integer
Dim Count2 As Integer
Dim Count3 As Integer
Dim NewInt As Integer
Dim ValOne As Integer
Dim ValTwo As Integer
ListBox1.Items.Clear()
ListBox2.Items.Clear()
ListBox3.Items.Clear()
NewInt = 0
ValOne = 0
ValTwo = 0
ListBox1.Items.AddRange(Players.Text.Split(vbNewLine))
ListBox2.Items.AddRange(Goals.Text.Split(vbNewLine))
ListBox3.Items.AddRange(Positions.Text.Split(vbNewLine))
Count1 = ListBox1.Items.Count
Count2 = ListBox2.Items.Count
Count3 = ListBox3.Items.Count
If Count1 = Count2 And Count1 = Count3 And Count2 = Count3 Then
'Set two counters to compare all words with each other
For iFirstCounter As Integer = 0 To ListBox1.Items.Count - 1
For iSecondCounter As Integer = 0 To ListBox1.Items.Count - 1
'Make sure there will not be an 'out of range' error,
'because you are removing items from the listbox.
iSecondCounter = Convert.ToInt64(iSecondCounter)
iFirstCounter = Convert.ToInt64(iFirstCounter)
ListBox2.Items.RemoveAt(iSecondCounter)
ListBox2.Items.RemoveAt(iFirstCounter)
If iFirstCounter < iSecondCounter Then
ListBox2.Items.Insert(iFirstCounter, NewInt.ToString)
Else
ListBox2.Items.Insert(iSecondCounter, NewInt.ToString)
End If
Next
Next
Players.Text = ""
Goals.Text = ""
Positions.Text = ""
Dim i As Integer
For i = 0 To ListBox1.Items.Count - 1
If Players.Text = "" Then
Players.Text = ListBox1.Items(i)
Else
Players.Text = Players.Text & vbNewLine & ListBox1.Items(i)
End If
Next
Dim a As Integer
For a = 0 To ListBox2.Items.Count - 1
If Goals.Text = "" Then
Goals.Text = ListBox2.Items(a)
Else
Goals.Text = Goals.Text & vbNewLine & ListBox2.Items(a)
End If
Next
Dim b As Integer
For b = 0 To ListBox3.Items.Count - 1
If Positions.Text = "" Then
Positions.Text = ListBox3.Items(b)
Else
Positions.Text = Positions.Text & vbNewLine & ListBox3.Items(b)
End If
Next
Else
MessageBox.Show("The Text Boxes don't contain an equal number of values ... please add more/remove some values")
End If
Could be done in multiple ways, for example:
If TextBox2.Lines.Count > 1 Then
Dim LineList As List(Of String) = TextBox2.Lines.ToList 'textbox lines
Dim NewLines As List(Of String) = TextBox2.Lines.ToList 'can't edit list we're looping over, a copy of lines
Dim NamesList As New List(Of String)
For x = 0 To LineList.Count - 1
Dim linesplit As String() = LineList(x).Split({" "}, StringSplitOptions.RemoveEmptyEntries)
If NamesList.Contains(linesplit(0)) Then
NewLines.Remove(LineList(x))
Else
NamesList.Add(linesplit(0))
End If
Next
TextBox2.Lines = NewLines.ToArray
End If
Here's an example of code that does this via LINQ and Lambdas.
Module Module1
Sub Main()
Dim ungroupedPlayers(1) As String
ungroupedPlayers(0) = "Jordan 26 Center"
ungroupedPlayers(1) = "Jordan 4 Center"
Dim players = ungroupedPlayers.ToList().ConvertAll(Of Player)(Function(x As String) As Player
Dim split() As String = x.Split(" "c)
Dim p As New Player
p.PlayerName = split(0)
p.Count = split(1)
p.Position = split(2)
Return p
End Function)
Dim playersGrouped = From p In players
Group By PlayerName = p.PlayerName Into g = Group
Select PlayerName, Count = g.Sum(Function(ip As Player) ip.Count), Position = g.Min(Function(ip As Player) ip.Position.ToString())
Dim groupedPlayers() As String = playersGrouped.ToList().ConvertAll(Of String)(Function(ip)
Return ip.PlayerName.ToString() & " " & ip.Count.ToString() & " " & ip.Position.ToString()
End Function).ToArray()
For Each groupedPlayer as String in groupedPlayers
Console.WriteLine(groupedPlayer)
Next
Console.Read()
End Sub
Public Class Player
Public PlayerName As String
Public Count As Integer
Public Position As String
End Class
End Module
You don't need heavy ListBox control for working with players data.
Use List(Of T) and create class Player for better readability.
You can remove duplicates before you will display values in your form.
And instead of multiline textbox you can use DataGridView as "right tool for the editing data".
Public Class Player
Public Property Name As String
Public Property Position As String
Public Property Goals As Integer
End
Public Class PlayersForm : Form
Private Sub Form_Load(sender As Object, e As System.EventArgs) Handles MyBase.Load
Dim data As List(Of Player) = LoadPlayersData()
Dim players As List(Of Player) = NormalizeData(data)
' Use DataGridView
Me.DataGridView1.DataSource = players
End Sub
Private Function LoadPlayersData() As List(Of Player)
Dim rawData As String() = File.ReadAllLines("pathToTextFile")
Return rawData.Select(Function(line) LineToPlayer(line)).ToList()
End Function
Private Function NormalizeData(players As List(Of Player)) As List(Of Player)
Return players.Group(Function(player) player.Name)
.Select(Function(group)
Return New Player With
{
.Name = group.Key,
.Position = group.First().Position,
.Goals = group.Sum(Function(player) player.Goals)
}
End Function)
.ToList()
End Function
Private Function LineToPlayer(line As String) As Player
Dim values = line.Split(" "c)
Return New Player With
{
.Name = values(0),
.Position = values(2),
.Goals = Integer.Parse(values(1))
}
End Function
End Class
DataGridView control will automatically update your List(Of Players) when you make any change. which give you possibility to have some other controls which automatically display best scorers for example, without extra converting data from string to integer and back.

Getting only records not in second table LINQ

i need help figuring out how to do this, i have a DataSet that contains records by Date and currencyCode, what i want is to compare two datasets and know if one (left table) have records that don´t exists on the second one (right table), i have the following code for joining the table based on date and code:
vLINQ = (From ContentExchangeRates In myDataset.Tables(0).AsEnumerable() _
Join PartnerExchangeRates In PartnerDataSet.Tables(0).AsEnumerable() _
On ContentExchangeRates.Field(Of Date)("EffectiveDate") Equals PartnerExchangeRates.Field(Of Date)("RateDate") _
And ContentExchangeRates.Field(Of String)("CurrencyCode") Equals PartnerExchangeRates.Field(Of Date)("CurrencyCode") _
Select New With { .PartnerRateDate = PartnerExchangeRates.Field(Of Date)("RateDate"), _ etc}).ToList
Using Jeff answer the code looks like this:
vLINQ = (From ContentExchangeRates In myDataSet.Tables(0).AsEnumerable() _
Group Join PartnerExchangeRates In PartnerDataSet.Tables(0).AsEnumerable() _
On PartnerExchangeRates.Field(Of Date)("RateDate") Equals ContentExchangeRates.Field(Of Date)("EffectiveDate") _
And PartnerExchangeRates.Field(Of String)("CurrencyCode") Equals ContentExchangeRates.Field(Of String)("ConvertCurrencyCode") _
Into result = Group From m In result.DefaultIfEmpty _
Select New With _
{ _
.PartnerRateDate = m.Field(Of Date)("RateDate") _
}).ToList
But i get the following error: Value cannot be null.
Parameter name: row
I want to be able to make a left outer join, that will only retrieve records that do not exists on PartnerExchangeRates table, but i don´t know what to do any more :(
Any help is appreciated!
To perform a left outer join, you need to do a group join and go through the results of the group join and use DefaultIfEmpty. It will translate to a left outer join.
Dim query =
From cer In myDataset.Tables(0).AsEnumerable
Group Join per In PartnerDataSet.Tables(0).AsEnumerable
On New With
{
.Date = cer.Field(Of Date)("EffectiveDate"),
.Currency = cer.Field(Of String)("CurrencyCode")
}
Equals New With
{
.Date = per.Field(Of Date)("RateDate"),
.Currency = per.Field(Of String)("CurrencyCode")
}
Into pers = Group
From per in pers.DefaultIfEmpty
Where per Is Nothing
Select PartnerRateDate = per.Field(Of Date)("RateDate"), ...
You can use the Enumerable.Except(IEnumerable, IEnumerable, IEqualityComparer) method:
Option Infer On
Module Module1
Class RowComparer : Implements IEqualityComparer(Of DataRow)
Public Function Equals1(x As DataRow, y As DataRow) As Boolean Implements IEqualityComparer(Of DataRow).Equals
' use .Equals for the DateTime and Is for the String
Return x(0).Equals(y(0)) AndAlso x(1) Is y(1)
End Function
Public Function GetHashCode1(obj As DataRow) As Integer Implements IEqualityComparer(Of DataRow).GetHashCode
Return obj(0).GetHashCode Xor obj(1).GetHashCode
End Function
End Class
Sub ShowDt(dt As DataTable)
For Each r As DataRow In dt.Rows
Console.WriteLine(CDate(r(0)).ToString("yyyy-MM-dd") & " " & CStr(r(1)))
Next
Console.WriteLine("-------------")
End Sub
Sub Main()
Dim dt(1) As DataTable
For i = 0 To 1
dt(i) = New DataTable
dt(i).Columns.Add(New DataColumn With {.ColumnName = "EffectiveDate", .DataType = Type.GetType("System.DateTime")})
dt(i).Columns.Add(New DataColumn With {.ColumnName = "CurrencyCode", .DataType = Type.GetType("System.String")})
Next
' test data
For i = 0 To 1
For j = 1 To 10
Dim nr = dt(i).NewRow
nr("EffectiveDate") = New DateTime(2015, 1, j)
nr("CurrencyCode") = "USD"
dt(i).Rows.Add(nr)
Next
Next
' remove a couple of rows from the second table
dt(1).Rows.RemoveAt(9)
dt(1).Rows.RemoveAt(2)
ShowDt(dt(0))
ShowDt(dt(1))
' find the rows in dt(0) which are not in dt(1)...
Dim missings = (dt(0).AsEnumerable.Except(dt(1).AsEnumerable, New RowComparer())).CopyToDataTable()
ShowDt(missings)
Console.ReadLine()
End Sub
End Module
But if it happens that your data is amenable to being stored in lists, your code could be cleaner and more readable:
Option Infer On
Module Module1
Public Class DateAndCurrency
Public Property EffectiveDate As DateTime
Public Property CurrencyCode As String
Public Overrides Function ToString() As String
Return Me.EffectiveDate.ToString("yyyy-MM-dd") & " " & Me.CurrencyCode
End Function
End Class
Public Class DateAndCurrencyComparer : Implements IEqualityComparer(Of DateAndCurrency)
Public Function Equals1(x As DateAndCurrency, y As DateAndCurrency) As Boolean Implements IEqualityComparer(Of DateAndCurrency).Equals
Return x.CurrencyCode = y.CurrencyCode AndAlso x.EffectiveDate = y.EffectiveDate
End Function
Public Function GetHashCode1(obj As DateAndCurrency) As Integer Implements IEqualityComparer(Of DateAndCurrency).GetHashCode
Return obj.CurrencyCode.GetHashCode Xor obj.EffectiveDate.GetHashCode
End Function
End Class
Sub Main()
' test data
Dim daccs As New List(Of List(Of DateAndCurrency))
For i = 0 To 1
Dim dacc As New List(Of DateAndCurrency)
For j = 1 To 10
dacc.Add(New DateAndCurrency With {.EffectiveDate = New DateTime(2015, 1, j), .CurrencyCode = "USD"})
Next
daccs.Add(dacc)
Next
' remove a couple of rows from the second list
daccs(1).RemoveAt(9)
daccs(1).RemoveAt(2)
For i = 0 To 1
daccs(i).ForEach(Sub(x) Console.WriteLine(x.ToString()))
Console.WriteLine("-------------")
Next
' find the items in daccs(0) which are not in daccs(1)...
Dim missings = daccs(0).Except(daccs(1), New DateAndCurrencyComparer())
missings.ToList().ForEach(Sub(x) Console.WriteLine(x.ToString()))
Console.WriteLine("-------------")
Console.ReadLine()
End Sub
End Module

Visual Basic Confusion

I have been required to create a program that asks me to find the maximum value of one particular array. I am using multiple forms in this project and have used a user-defined data type and created multiple array under it. There is a first form that is related to this, which defines my defined data type is gStudentRecord and the arrays that define it are last name, Id, and GPA. This second form is where I write all of the code to display what I want. My question is how to get the Max GPA out of that array. I'm sorry if this isn't in very good format, this is the first time I've used Stackoverflow
Public Class frmSecond
Private Sub frmSecond_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim Ctr As Integer
Dim Line As String
lstDisplay.Items.Clear()
lstDisplay.Items.Add("Name".PadRight(25) & "ID".PadRight(16) & "GPA".PadRight(20) & "Out of state".PadRight(10))
For Ctr = 0 To gIndex Step 1
Line = gCourseRoster(Ctr).LastName.PadRight(20) & gCourseRoster(Ctr).ID.PadRight(15) & gCourseRoster(Ctr).GPA.ToString.PadRight(15) & gCourseRoster(Ctr).OutOfState.ToString().PadLeft(5)
lstDisplay.Items.Add(Line)
Next
End Sub
Private Sub btnStats_Click(sender As Object, e As EventArgs) Handles btnStats.Click
Dim Ctr As Integer = 0
Dim Average As Double
Dim Sum As Double
Dim Found As Boolean = False
Dim Pass As Integer
Dim Index As Integer
lstDisplay.Items.Clear()
**For Ctr = 0 To gIndex Step 1
If gCourseRoster(Ctr).GPA > gCourseRoster(Ctr).GPA Then
lstDisplay.Items.Add(gCourseRoster(Ctr).GPA)
End If
Next**
Average = gComputeAverage(Sum)
lstDisplay.Items.Add("Number of Students: " & gNumberOfStudents)
lstDisplay.Items.Add("Average: " & Average)
End Sub
Private Function gComputeAverage(Sum As Double) As Double
Dim Ctr As Integer
Dim Average As Double
For Ctr = 0 To gIndex Step 1
Sum = Sum + gCourseRoster(Ctr).GPA
Next
Average = Sum / gNumberOfStudents
Return Average
End Function
End Class
You can use a Lambda expression to tease it out. The Cast part is converting from the gCourseRoster to a collection of Double by supplying the GPA to the Select statement.
Dim gList As New List(Of gCourseRoster)
gList.Add(New gCourseRoster With {.id = 1, .name = "Bob", .GPA = 3.9})
gList.Add(New gCourseRoster With {.id = 2, .name = "Sarah", .GPA = 3.2})
gList.Add(New gCourseRoster With {.id = 3, .name = "Frank", .GPA = 3.1})
Dim maxGPA = gList.Cast(Of gCourseRoster).Select(Function(c) c.GPA).ToList.Max
MessageBox.Show(maxGPA.ToString)
Output: 3.9

Storing Objects into a Class & Retrieving upon Request

I seriously need help with my project.
I am trying to store specific jobs into a Class, which then displays in a List Box.
When selecting the List Box, I want the rest of the information to be displayed into a Text Box.
I can add Jobs into the List Box, and the Report button sorts the Job by Earliest to Latest.
I just CANNOT seem to code the Display Button to retrieve the rest of the information.
http://i.stack.imgur.com/0eV5j.png
What am I doing wrong?
My Code:
Public Class Form1
Dim jobList As List(Of UserInformation) = New List(Of UserInformation)
Dim j As UserInformation = New UserInformation()
Private Sub btnReport_Click(sender As Object, e As EventArgs) Handles btnReport.Click
Dim p As UserInformation = New UserInformation()
Dim qty As Integer = jobList.Count - 1
Dim name(qty) As String
Dim deadline(qty) As Date
Dim i As Integer = 0
'fill the array
For i = 0 To qty
p = jobList(i)
name(i) = p.Name
deadline(i) = p.Deadline
Next
'sort the array
Dim done As Boolean = False
While done = False
done = True
For i = 0 To qty - 1
Dim tempName As String
Dim tempDate As Date
If deadline(i) > deadline(i + 1) Then
tempName = name(i)
tempDate = deadline(i)
name(i) = name(i + 1)
deadline(i) = deadline(i + 1)
name(i + 1) = tempName
deadline(i + 1) = tempDate
done = False
End If
Next
End While
lsbReport.Items.Clear()
lblListbox.Text = "List in date order"
For i = 0 To name.Length - 1
Dim str As String
str = name(i) + ", "
str += deadline(i).ToString + "."
lsbReport.Items.Add(str)
Next
End Sub
Private Sub updateListBox()
lsbReport.Items.Clear()
lblListbox.Text = "All people in the List"
For Each person As UserInformation In jobList
Dim str As String
str = person.Name + ", "
str += person.Deadline.ToString + "."
lsbReport.Items.Add(str)
Next
End Sub
Private Sub btnAdd_Click(sender As Object, e As EventArgs) Handles btnAdd.Click
Dim p As UserInformation = New UserInformation()
p.Name = firstNameText.Text
p.Deadline = lastNameText.Value
jobList.Add(p)
updateListBox()
End Sub
Private Sub btnDisplay_Click(sender As Object, e As EventArgs) Handles btnDisplay.Click
Dim job_info As UserInformation = CType(lsbReport.SelectedItem(), UserInformation)
txtReport.Text = "Job Title: " & job_info.Name() & Environment.NewLine
txtReport.Text &="Job DeadLine: " & job_info.Deadline & Environment.NewLine
txtReport.Text &="Job Description" & job_info.Description
End Sub
End Class
Public Class UserInformation
Public job_deadline As Date
Public job_name As String
Public job_description As String
Public Property Name() As String
Get
Return job_name
End Get
Set(ByVal value As String)
job_name = value
End Set
End Property
Public Property Deadline() As String
Get
Return job_deadline
End Get
Set(ByVal value As String)
job_deadline = value
End Set
End Property
Public Property Description() As String
Get
Return job_description
End Get
Set(ByVal value As String)
job_description = value
End Set
End Property
End Class
The bottom line is that you stored string values to the LBs:
str = person.Name & ", " & person.Deadline.ToString
lsbReport.Items.Add(str)
So, that is what you get back making it difficult to connect the string created with the object it represents.
Listboxes and comboboxes can store objects as well as strings. Simple Demo:
Public Class Employee
Public Property ID As Integer ' a db ID maybe
Public Property Name As String
Public Property Department As String
Public Property HireDate As Date
Public Overrides Function ToString As String
Return Name ' text to show in controls
' in a more realistic class it might be:
' Return String.Format("{0}, {1} ({2})", LastName,
' FirstName, BadgeNumber)
' e.g. "Whitman, Ziggy (123450)"
End Function
End Class
Friend EmpList As New List(of Employee) ' if needed
storing an object to a listbox is simple:
Dim newEmp As Employee
newEmp.Name = "Ziggy"
' set props as needed
myLB.Items.Add(newEmp) ' add to a ListBox directly.
Once you have a class for these things, you have many options. You can store them to a List(Of T) which can be used with a List or ComboBox:
Private EmpList As New List(Of Employee)
...
EmpList.Add(newEmp) ' adding to a list is same as a ListBox
' add from List to a control:
myLB.Items.AddRange(Emps.ToArray)
myLB.SelectedItem will now be an Employee object, which should make displaying details elsewhere simple.
To make it even more efficent, you can use the list as a DataSource so you dont have to add references to the listbox:
myLB.DataSource = EmpList
myLB.DisplayMember = "Name" ' the property to display
myLB.ValueMember = "Id" ' what to use for SelectedValue

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