Find the indices of matching elements of two lists - vb.net

vb.NET
I have two lists (listA and listB) and want to return a new list (listC) containing listB’s indices of the matching elements when compared to listA.
So, let’s say: listA = 3,4,5 and listB = 1,2,3,4,5,6,7. I’d like listC to return 2,3,4 because those are the indices of the matches in listB.
The following code will return the matching values of listA and listB in listC.
Dim commonlist = listA.Intersect(listB).ToList()
For each common As String In commonlist
listC.Add(common)
Console.WriteLine(common)
Next common
How can I return the indices to listC, or a new listD for that matter?

Dim listC = listA.Select(Function(itemA) listB.IndexOf(itemA)).ToList()
In case you're not familiar with LINQ, the Select method takes a function to map items in a list to a new list. It is completely up to you what that mapping function does. It's commonly getting a single property value from the items but, in your case, it's getting the index of the items in the other list.
Select returns an IEnumerable(Of T) and that's all you need if all you plan to do is run a For Each loop over it, but you can call ToList to create a List(Of T) if that's what you want.
EDIT:
I think that I may have misinterpreted the question slightly. I'll leave my original answer as what I have done is basically the listD requested in the question but I'll add information about getting the items and the indexes.
Here is one option:
Dim listA As New List(Of Integer) From {1, 2, 3, 9}
Dim listB As New List(Of Integer) From {1, 2, 3, 4, 5, 6}
Dim listC = listA.Select(Function(a) Tuple.Create(a, listB.IndexOf(a))).
Where(Function(ta) ta.Item2 <> -1).
ToList()
For Each ta In listC
Console.WriteLine("Value: {0}; Index: {1}", ta.Item1, ta.Item2)
Next
That code will create a list of Tuples where each one contains a value from listA and the index of that value in listB. Those values not in both lists are excluded.
If you want something a bit nicer than a Tuple then you can create a Dictionary:
Dim listA As New List(Of Integer) From {1, 2, 3, 9}
Dim listB As New List(Of Integer) From {1, 2, 3, 4, 5, 6}
Dim dictionaryC = listA.Select(Function(a) Tuple.Create(a, listB.IndexOf(a))).
Where(Function(ta) ta.Item2 <> -1).
ToDictionary(Function(ta) ta.Item1,
Function(ta) ta.Item2)
For Each key In dictionaryC.Keys
Console.WriteLine("Value: {0}; Index: {1}", key, dictionaryC(key))
Next
If you specifically want two separate lists then you can do this:
Dim listA As New List(Of Integer) From {1, 2, 3, 9}
Dim listB As New List(Of Integer) From {1, 2, 3, 4, 5, 6}
Dim listC = listA.Intersect(listB).ToList()
Dim listD = listC.Select(Function(c) listB.IndexOf(c)).ToList()
For i = 0 To listC.Count - 1
Console.WriteLine("Value: {0}; Index: {1}", listC(i), listD(i))
Next

Related

How to get the indices of sorted list in VB.net?

I have a list as shown below:
Dim mList As New List(Of Double)
mList.AddRange({3, 2, 5, 3, 6})
Now i want to sort it and get the indices of sorted list. For sorting, i can use
mList.sort()
but how to get the indices of sorted list i.e. (1,0,3,2,4)
As it stands, you can't. When you sort a List, it doesn't keep any record of the previous indexes of the items and why would it? It's up to you to do that. There are various options available to you. One is to create an array containing the original items and another array containing the original indexes, then sort them both based on the items array. The index array will then indicate the original indexes of the items. E.g.
Dim itemsList As New List(Of Integer) From {3, 2, 5, 3, 6}
Dim itemsArray = itemsList.ToArray()
Dim indexesArray = Enumerable.Range(0, itemsArray.Length).ToArray()
Array.Sort(itemsArray, indexesArray)
itemsList.Clear()
itemsList.AddRange(itemsArray)
For i = 0 To itemsList.Count - 1
Console.WriteLine($"Current index: {i}; Value: {itemsList(i)}; Original index: {indexesArray(i)}")
Next

Show MsgBox() data from two arrays in a loop

I'm trying to write code to show data from two arrays via a MsgBox(). I have the code below, but of course it doesn't work:
Dim numbers() As Integer = {1, 4, 7}
Dim letters() As String = {"a", "b", "c"}
' Iterate through the list by using nested loops.
For Each number As Integer In numbers and For Each letter As String In letters
MsgBox(number.ToString & letter & " ")
Next
What do I need to do to get output that looks like this? :
1a
4b
7c
You need a For loop that uses an index rather than a For Each loop:
Dim numbers() As Integer = {1, 4, 7}
Dim letters() As String = {"a", "b", "c"}
For i As Integer = 0 To numbers.Length - 1
MsgBox(numbers(i) & letters(i))
Next
You can also use the Zip() linq operator:
For Each output As String In numbers.Zip(letters, Function(n, l) n & l)
MsgBox(output)
Next
You may benefit from something like a dictionary. Not sure what you're trying to accomplish combining 2 arrays arbitrarily but something like this may be better:
Dim Dict As New Dictionary(Of Integer, String) From {{1, "a"}, {4, "b"}, {7, "c"}}
For Each Item In Dict
MsgBox(Item.Key & Item.Value)
Next
That would allow you to lookup items based on the ID (integer) with Linq such as:
Dict.Where(Function(x) x.Key = 1).SingleOrDefault and grab the key/value pair.

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 check if a List A is a sub-list in List B using VB.net?

Do I have to write my own Function ?
Is there a built in Function in List to checks another list for membership ?
Dim ListA As New List(Of Int32)(New Int32() {1, 3, 5})
Dim ListB As New List(Of Int32)(New Int32() {1, 2, 4, 6, 5, 3, 7})
Dim isSubList = Not ListA.Except(ListB).Any() 'True'
MSDN: Enumerable.Except

Getting the index of the largest integer in an array

I have an array of integers and I need to know the index of the largest number (not the actual value, just the index of whichever is highest).
However, if one or more indexes "tie" for the highest value, I need to have all of the indexes that share that high value.
I assume this function would need to return an array (since it could be one or more indexes), but I am not totally sure how to go about getting the more efficient solution.
If this is going to be a common thing you could write your own Extension. You should add some additional sanity/null checking but this will get you started:
Module Extensions
<System.Runtime.CompilerServices.Extension()> Function FindAllIndexes(Of T)(ByVal array() As T, ByVal match As Predicate(Of T)) As Integer()
''//Our return object
Dim Ret As New List(Of Integer)
''//Current array index
Dim I As Integer = -1
''//Infinite loop, break out when we no more matches are found
Do While True
''//Loop for a match based on the last index found, add 1 so we dont keep returning the same value
I = System.Array.FindIndex(array, I + 1, match)
''//If we found something
If I >= 0 Then
''//Append to return object
Ret.Add(I)
Else
''//Otherwise break out of loop
Exit Do
End If
Loop
''//Return our array
Return Ret.ToArray()
End Function
End Module
Then to call it:
Dim ints As Integer() = New Integer() {1, 2, 8, 6, 8, 1, 4}
Dim S = ints.FindAllIndexes(Function(c) c = ints.Max())
''//S now holds 2 and 4
If you are using .NET 3.5, you can use the Max() Extension function to easily find the highest value, and use Where to locate the matching records in your source array.
IList has an IndexOf member, which helps. This code is completely untested, and probably has at least one off-by-one error.
Public Function GetPostionsOfMaxValue(ByVal input() As Integer) As Integer()
Dim ints = New List(Of Integer)(input)
Dim maxval = ints.Max
Dim indexes As New List(Of Integer)
Dim searchStart As Integer = 0
Do Until searchStart >= ints.Count
Dim index = ints.IndexOf(maxval, searchStart)
If index = -1 Then Exit Do
indexes.Add(index)
searchStart = index + 1
Loop
Return indexes.ToArray
End Function