Associating a list with another that has been sorted - vb.net

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 }

Related

How to get a collection of elements in my dictionary that meet a given condition?

I am going through some pluralsight videos and I found this amazing one called "Beautiful C++ 14: STL Alorithms". In this video the instructor discusses using the a variation of find_if methods like the one below.
vector<int> v{ 4, 6, 6, 1, 3, -2, 0, 11, 2, 3, 2, 4, 4, 2, 4 };
//find the first zero in the collection
auto result = find(begin(v), end(v), 0);
These methods are fantastic and I will be using it for some of my C projects, but I am currently working in a VB project where I need some kind of the same functionality.
I have a dictionary and I need to get certain objects out of it based on an if statement.
Structure Example_Struct
Public ExampleItem1 As Integer
Public ExampleItem2 As Integer
End Structure
Private Example_Dictionary As New Generic.Dictionary(Of String, Example_Struct)
Private Sub PopulateDictionaryWithDummyData()
Dim dummyData1 As Example_Struct
Dim dummyData2 As Example_Struct
dummyData1.ExampleItem1 = 1
dummyData1.ExampleItem2 = 1
dummyData1.ExampleItem1 = 5
dummyData1.ExampleItem2 = 5
Example_Dictionary.Add("Data1", dummyData1)
Example_Dictionary.Add("Data2", dummyData2)
End Sub
Private Sub LoadData()
PopulateDictionaryWithDummyData()
Dim stIdx As Integer
Dim myStruct As New Example_Struct
Dim item1 As Integer = 5
Dim item2 As Integer = 5
Dim count As Integer = Example_Dictionary.Count
For stIdx = 1 To count
' Get the stand data
myStruct = Example_Dictionary.ElementAt(stIdx).Value
If (myStruct.ExampleItem1 = item1 And myStruct.ExampleItem1 = item2) Then
' Do Something
End If
Next
End Sub
Above is some sample code to test with were I populate my Example_Dictionary with some dummy data. Then loop through the dictionary and put the comment where I need to do some things.
I would like my code to take the for loop out of the picture completley, just grab the "Example_Struct" that matches my conditions.
If there are more than one "Example_Struct" that matches my condition, I would need it to return a collection of these.
To filter the dictionary values based on a certain condition, you may use something like the following:
Dim myStructs = Example_Dictionary.Values.
Where(Function(x) x.ExampleItem1 = item1 AndAlso x.ExampleItem2 = item2).ToList()
Note that when dealing with logical operations, it's always a good idea to use the short-circuit logical operators (i.e., AndAlso instead of And and OrElse instead of Or). You can learn more about the difference in this guide:
Logical and Bitwise Operators in Visual Basic

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

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

Split array on special item and take the items then into different lists

Heyho, I am trying to split an array at a special position and then add the items inbetween into 3 different lists/arrays.
For example I try to do something like that:
Test1
Test2
--
Test3
Test4
--
Test5
Test6
I want to "split" it at the "--"-items to get the items inbetween.
Then I want to add them to different arrays, like that:
Array1:
Test1
Test2
Array2:
Test3
Test4
Array3:
Test5
Test6
I have already declared the different arrays and a source array that is filled via File.ReadAllLines.
Like that:
Dim source As String() = File.ReadAllLines(filePath)
Dim array1 As String() = New String() {}
Dim array2 As String() = New String() {}
Dim array3 As String() = New String() {}
So, I have tried to do something with Array.Copy(), but I did not manage to do it.
Anyway, has someone a working way/solution? To add it to a list then is not the problem, because AddRange wants an IEnumerable(Of T), so I can easily add this then.
Help is appreciated.
On questions leave a comment and ask. I tried to explain it as good as I could. ;)
It will be much easier if you use lists to hold the values. So you have a List(Of String) for each section, and have a List of those to contain all the sections. When you encounter a line which is "--", start a new list for a new section.
Imports System.IO
Module Module1
Sub Main()
Dim src As String = "C:\temp\testtext.txt"
Dim testSections As New List(Of List(Of String))
Using rdr As New StreamReader(src)
Dim thisSection As New List(Of String)
While Not rdr.EndOfStream
Dim thisLine = rdr.ReadLine
If thisLine = "--" Then
' don't add empty sections
If thisSection.Count > 0 Then
testSections.Add(thisSection)
thisSection = New List(Of String)
End If
Else
thisSection.Add(thisLine)
End If
End While
' add on the last one
If thisSection.Count > 0 Then
testSections.Add(thisSection)
End If
End Using
' show the data
For i As Integer = 0 To testSections.Count - 1
Console.WriteLine("List " & i.ToString())
For j As Integer = 0 To testSections(i).Count - 1
Console.WriteLine(testSections(i)(j))
Next
Console.WriteLine()
Next
Console.ReadLine()
End Sub
End Module
Using your sample data, it outputs:
List 0
Test1
Test2
List 1
Test3
Test4
List 2
Test5
Test6
Of course, you will put the code in a Sub or Function to make it tidy.
I am assuming that the source file can hold a variable number of sections. Therefore I will be using a List(Of Integer) to hold the split positions. Also I will add additional virtual split positions before the first and after the last line, this makes it easier to calculate the length of the parts, as they always will be enclosed between two split positions.
Also, since we can have a variable number of sections, we are going to store them in an array of arrays of strings instead of using individual array variables. Further, this has the advantage that we can do the whole copying process in a loop.
Dim source As String() = File.ReadAllLines(filePath)
' Create a list that holds split positions.
Dim splitPositions = New List(Of Integer)()
' Add an additional split position at the beginning of the list.
splitPositions.Add(-1)
' Get split positions.
For i As Integer = 0 To source.Length - 1
If source(i) = "--" Then
splitPositions.Add(i)
End If
Next
' Add an additional split position at the end of the list.
splitPositions.Add(source.Length)
' Now all the parts are enclosed between split positions.
' Lets create an array that can hold the parts.
Dim parts = New String(splitPositions.Count - 2)() {}
' Now split.
For i As Integer = 0 To splitPositions.Count - 2
Dim length As Integer = splitPositions(i + 1) - splitPositions(i) - 1
parts(i) = New String(length - 1) {}
Array.Copy(source, splitPositions(i) + 1, parts(i), 0, length)
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))