Count of each unique value in a list - vb.net

I need to find the unique values from a list and the number of times each value occurred in the original list. Here is what i have so far:
Dim Lister As New List(Of String)()
For Each item In eColumn
Lister.Add(item.value)
Next
Dim Result As New List(Of String)
Result = Lister.Distinct().ToList
For Each st In Result
MsgBox(st)
Next
The result is a list of all the unique values, but does not include a count for each item. For example, if my list was
John
John
Abbey
Larry
Larry
Larry
Charles
I want 4 values returned: John = 2, Abbey = 1,Larry = 3, Charles = 1.

Using linq's .Distinct() is only going to give you a list containing each distinct name in your list; so as you must have seen when your messagebox loop ran, it just shows you each name that is in your list once.
VB's lists do not have a native function for returning the count of an item's occurrences in a list, so to achieve your desired result just group them using linq's .GroupBy() function. It will return a Linq.GroupedEnumerable object, which can be iterated through and also possesses the kind of count property you're looking for:
Dim myList As New List(Of String) From {"John", "John", "Abbey", "Larry", "Larry", "Larry", "Charles"}
Dim groupedNames = myList.GroupBy(Function(x) x)
If groupedNames IsNot Nothing AndAlso groupedNames.Count > 0 Then
For Each person In groupedNames
Debug.Print(person.Key & "-" & person.Count.ToString)
Next
End If
Output:
John-2
Abbey-1
Larry-3
Charles-1

Related

Get ListBox and TextBox Matching Values VB.NET

I have a listbox1 containing some values
value 1
value 2
value 3
value 4
value 5
and TextBox1 containing some lines
Value 3
Value 5
Value 10
Value 14
I want to get values matching in listbox1 and TextBox1
like
Value 3
Value 5
and perform some action code if values matches and loop until last value match
I have used this code but its not giving accurate output.
Dim compare As String
Dim comparear() As String
Dim list As String
Dim listar() As String
compare = TextBox1.Text
comparear = compare.Split(vbNewLine)
list = TextBox2.Text
listar = list.Split(vbNewLine)
For i = 0 To comparear.Length - 1
For p = 0 To listar.Length - 1
If listar(p).Contains(comparear(i)) Then
txt_match.Text = txt_match.Text & listar(p) & vbNewLine
Else
End If
Next
Next
You can write
Dim result = listBox1.Items.OfType(Of String).Intersect(textBox1.Lines)
result is of type IEnumerable(Of String). I.e., you can use it in For Each or add .ToList or .ToArray to get a collection.
Note that the match is case sensitive. If you want to ignore the case, you can write
Dim result = listBox1.Items.OfType(Of String) _
.Intersect(textBox1.Lines, StringComparer.OrdinalIgnoreCase)
Since the ListBox Items are returned as Objects, I used listBox1.Items.OfType(Of String) to convert them to Strings.
As I can see in the code example that you have added, you are comparing the lines of 2 TextBoxes and joning the result into a 3rd one, You can do it with these 2 code lines
Dim result = textBox1.Lines.Intersect(textBox2.Lines, StringComparer.OrdinalIgnoreCase)
txt_match.Text = String.Join(vbNewLine, result)
If you wanted to compare the items of 2 ListBoxes
Dim r = listBox1.Items.OfType(Of String) _
.Intersect(listBox2.Items.OfType(Of String), StringComparer.OrdinalIgnoreCase)
Intersect works with any two enumerations or collections of the same type
A.Intersect(B)
returns a IEnumerable(Of T) where both A and B are themselves IEnumerable(Of T). So it does not matter whether you are using the lines of a TextBox, the Items of a ListBox casted to T or arrays or List(Of T) or anything else.
Your code does not work because of 2 different errors:
You have Option Strict Off. This hides an error showing you that you are calling the wrong overload of Split. VB tries to convert vbNewLine (which is a String) to a Char, and calls Split with the 1st char found in vbNewLine. Use Option Strict On in your project and you will get a compile error. Write this instead
comparear = compare.Split(New String() {vbNewLine}, StringSplitOptions.RemoveEmptyEntries)
listar = list.Split(New String() {vbNewLine}, StringSplitOptions.RemoveEmptyEntries)
Your match is case sensitive. Instead compare like this
If listar(p).ToLowerInvariant = comparear(i).ToLowerInvariant Then

Comparing three lists

I'm trying to compare three lists and from these lists, trying to find the unique element.
As an example:
List A: 1
List B: 1, 2
List C: 1, 2, 3
As a result, element "3" should be stored.
What I have attempted by looking at previous questions and expanding on is:
Dim result1 As List(Of Long) = A.Except(B).ToList()
Dim result2 As List(Of Long) = C.Except(result1).ToList()
The issue I face is my lists could be as such:
List A: 1
List B: 1
List C: 1, 2
As a result, result1 will store 0 which compares to list C and were then storing in result2: 1 and 2
Is it possible to compare three lists simultaneously? The except document on Microsoft states 2 lists only. Alternatively I could just do a pre check to see if A and B are the same and not to compare them but rather do a 3 way comparison.
How about:
ListA
.Concat(ListB)
.Concat(ListC)
.GroupBy(Function(i) i)
.Where(Function(g) Not g.Skip(1).Any())
.Select(Function(g) g.Key)
There are several ways to do this...
Dim listOfLists = new List(Of List(Of Long))()From { _
listA, _
listB, _
listC _
}
Dim resultList = listOfLists.Aggregate(Function(previousList, nextList) previousList.Intersect(nextList).ToList())
Jon Skeet has another approach, see Intersection of multiple lists with IEnumerable.Intersect() (C#)

Associating a list with another that has been sorted

I have two lists, one integer and one string. These values are entered during a loop, so they are associated together (e.g. ListOfInteger.Item(i) and ListOfString.Item(i) were entered at the same time and are related to each other). I sorted and subsequently reversed the list of integers. Is there any way to have the list of strings still associated with the list of integers, in order to display them in a text box. For example:
List of Strings (surname): List of Integers (score):
Jones 4
Perry 2
Morris 6
List of Strings (surname): Sorted List of Integers:
Jones 2
Perry 4
Morris 6
Edit:
If (name.Count + 1) < compnum Then
name.Add(txtName.Text)
score.Add(txtScore.Text)
Else
txtName.Text(Hide)
txtScore.Text(Hide)
btnSort.Text(Show)
End If
...
score.Sort()
score.Reverse()
txtSortedScore1.Text = score(0)
(and so forth)
How can I relate these two lists together in order to associate the data in the string list with the sorted list of integers?
Edit - The end result should look like this:
List of Strings (surname): Sorted List of Integers:
Perry 2
Jones 4
Morris 6
The techniques suggested by the others to use a container class or dictionary are better solutions. However to answer the question as stated, what you seeking to do is perform a keyed sort. Unfortunately, the List(Of T) class does not provide this functionality; it is provided by the Array Class.
This is a bit convoluted as first you dump the two lists to arrays, sort the arrays, and finally recreate the lists with the sorted results.
Dim keysList As List(Of Int32) = New List(Of Integer)(New Int32() {4, 2, 6})
Dim itemsList As List(Of String) = New List(Of String)(New String() {"Jones", "Perry", "Morris"})
Dim keys As Int32() = keysList.ToArray
Dim items As String() = itemsList.ToArray
Array.Sort(keys:=keys, items:=items)
keysList = New List(Of Integer)(keys)
itemsList = New List(Of String)(items)
You should wrap your string and integer in another object.
Class Person
Public String SurName
Public Int Score
End Class
From there you can manipulate your objects in any way you like and then iterate over them and output the data as you like.
Something like this:
Dim persons = New List(Of Person)()
persons.Add(New Person() With { _
Key .SurName = "Jones", _
Key .Score = 4 _
})
For Each p As var In persons.OrderBy(Function(x) x.Score)
ListBox.Add(p.SurName + p.Score)
Next
If you would provide your code, we could help you more.
There are few ways the names can be sorted. Here is one way:
Dim names = {"Jones", "Perry", "Morris"}, score = {4, 2, 6}
Dim sortedIndexes = Enumerable.Range(0, score.Length).OrderBy(Function(i) score(i)).ToArray ' { 1, 0, 2 }
Dim sortedNames = sortedIndexes.Select(Function(i) names(i)).ToList ' { "Perry", "Jones", "Morris" }
Dim sortedScore = sortedIndexes.Select(Function(i) score(i)).ToList ' { 2, 4, 6 }

How to merge two list to have a distinct list without duplicate values in vb.net

I have this problem in vb.net. Lets say I got 2 Lists ListA and ListB both holds objects of same type.
Eg., one of the property of the object is ID. (ID is written in brackets)
ListA ListB
---------------------------
A(3818) A(3818)
B(3819) B(3819)
C(3820) C(3820)
D(3821) D(3821)
E(3823) F(0)
H(3824) G(0)
I(3825)
How do I merge these two Lists to have a new distinct list which holds objects only once whose ID matches and all other objects(whose ID dont match) are simply added to the new list.
Sample output be,
New List
--------
A(3818)
B(3819)
C(3820)
D(3821)
E(3823)
F(0)
G(0)
H(3824)
I(3825)
When I searched I found that AddRange() and Union are some of the methods to do the merge. But i am not able to find if this works for non standard objects(apart from Integer, String)
Use addRange() and then linq with distinct to filter out the duplicates.
Dim b = YourCollection.Distinct().ToList()
Could use a collection bucket
Dim oCol As New Collection
AddTitems(oCol, oListA)
AddTitems(oCol, olistB)
Public Function AddTitems(oSummaryList As Collection, oList As List(Of thing)) As Collection
For Each oThing As thing In oList
If Not oSummaryList.Contains(CStr(oThing.ID)) Then oSummaryList.Add(oList, CStr(oThing.ID))
Next
Return oSummaryList
End Function
Here are a couple simple functions that should do that for you. I'm not sure how efficient they are though. I don't think there is anything built in.
Private Function nameOfFunction(list1 as list(of type), list2 as list(of type)) as list(of type)
Dim result as new list(of type)
for a as integer = 0 to math.max(list1.count, list2.count) - 1 step 1
If a < list1.count AndAlso resultHasID(result, list1(a).ID) = False Then
result.add(list1(a))
end if
If a < list2.count AndAlso resultHasID(result, list2(a).ID) = False Then
result.add(list2(a))
end if
next
End Function
Private Function resultHasID(testList as list(of type), s as string) as boolean
Dim result as Boolean = False
for a as integer = 0 to testlist.count - 1 step 1
if(testlist(a).ID = s) then
result = true
exit for
End if
Next
Return result
End function
For each item as String in ListA
If Not ListB.Contains(item) Then
ListB.Add(item)
End If
Next

VisualBasic 2010 - merge 2 strings and read them as a variable

i have this code, and i want to access some variables.
Dim k1 as String = "Something"
Dim k2 as String = "Something"
... to k230
------------------Then i have this:
Dim rnd = New Random()
Dim nextValue = rnd.Next(230)
For i = 0 To 230
If nextValue = i Then
MsgBox('k+i') <--BUT READ THIS AS A VARIABLE.
End If
i = i + 1
Next
i readed some similar questions, but them doesn't apply to this case.
Consider using arrays here:
http://msdn.microsoft.com/en-us/library/vstudio/wak0wfyt.aspx
An array is a set of values that are logically related to each other,
such as the number of students in each grade in a grammar school.
By using an array, you can refer to these related values by the same
name, and use a number that’s called an index or subscript to tell
them apart. The individual values are called the elements of the
array. They’re contiguous from index 0 through the highest index
value.
Try using a Dictionary:
Dim k As New Dictionary(Of Integer, String)()
k.Add(1, "Something")
k.Add(2, "Something")
'... to 230
Messagebox.Show(k(i))