Comparing three lists - vb.net

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#)

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

Count of each unique value in a list

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

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 Sort a Generic Collection With a Where Clause

I am using an IEnumerable as the dataSource for a Repeater control. I need to sort the IEnumerable WHERE an entities property value equals a given value, then ORDER BY some other value. In other words, I want to sort an IEnumerable after some selected entity.
I am guessing I will have to implement the IComparer interface, but not sure where to start. Perhaps it could be as simple as having 2 IEnumerables; one with the entity I want displayed first, a second with the other entities, then concatenate them.
Dim myEnumerable As IEnumerable(Of myEntity) = dbContext.myEntity
myEnumerable.OrderBy(Function(x) x.propertyA **where x.propertyA = "value"**) _
.OrderBy(function(x) x.propertyB)
For simplicity of an example, I will use an IEnumerable(of String)
Dim myEnumerable As IEnumerable(Of String) = {"1", "2", "3", "40", "50"}
myEnumerable.OrderBy(Function(x) where x = "3" Then Order By x.length)
I am looking for the resulting sort to be
3
1
2
40
50
Try This:-
Dim query = myEnumerable.Where(Function(x) x = "3").Concat(myEnumerable.Where(Function(x) x <> "3").OrderBy(Function(x) x)).ToList()
C# Equivalent:-
var query = myEnumerable.Where(x => x == "3").Concat(myEnumerable.Where(x => x != "3").OrderBy(x => x)).ToList();

How to loop through two lists simultaneously?

I have referred to the following question at: Using foreach loop to iterate through two lists. My question is this, with regards to the chosen answer: Can the o.DoSomething be a comparison? As in:
For Each a in ListA.Concat(ListB)
If(a from ListA=a from ListB) Then
Do Something here
End If
Next
As you might've guessed, I'm using VB.Net and would like to know how I can do what I have shown here. That would basically be to iterate through a joined list separately/independently. Thanks!
Your question indicates that you need a Join operation, because it's not that you want to iterate over two lists, but you also want to match like items from one list to the other.
Dim joinedLists = From item1 In list1 _
Join item2 In list2 _
On item1.Bar Equals item2.Bar _
Select New With {item1, item2}
For Each pair In joinedLists
'Do work on combined item here'
'pair.item1'
'pair.item2'
Next
Other answers recommend Zip. That is simply a function that takes two sequences and produces a single result, much like join, but it is geared to work in a FIFO method over both lists. If you need connections made based on an equality, Join is the specifically built to be right tool for this job.
The answers to my question is-it-possible-to-iterate-over-two-ienumerable-objects-at-the-same-time may help
Dim listA As List(Of A)
Dim listb As List(Of B)
listA.Zip(listb, Function(a, b) a.property1 = b.property1).ForEach(AddressOf Print)
Shared Sub Print(ByVal s As A)
Console.WriteLine(s.Property1)
End Sub
In .Net 4 you could use Zip. Adapted from my answer to this question from a Python fan specifically asking for tuples - you could remove the tuples if you like.
Sub Main()
Dim arr1() As String = {"a", "b", "c"} '' will also work with Lists
Dim arr2() As String = {"1", "2", "3"}
For Each t In TupleSequence(arr1, arr2)
If t.Item1 = t.Item2 Then
'' Do something
End If
Next
Console.ReadLine()
End Sub
Function TupleSequence(Of T1, T2)(
ByVal seq1 As IEnumerable(Of T1),
ByVal seq2 As IEnumerable(Of T2)
) As IEnumerable(Of Tuple(Of T1, T2))
Return Enumerable.Zip(seq1, seq2,
Function(s1, s2) Tuple.Create(s1, s2)
)
End Function
On solution would be to have a dictionary with items from the second list, then loop through your first list and retrieve the corresponding item in the second one, using the dictionary. Here's an example, assuming you want to compare items with an ID property:
Dim DictB = ListB.ToDictionary(Function(x) x.ID)
For each itemA in ListA
If DictB.ContainsKey(itemA.ID)
'Item is in both lists
Dim itemB = DictB(itemA.ID)
'Do something here
End If
Next