How do I sort collection items by value in VB.NET?
I want to sort this:
Dim col as Collection = New Collection
col.Add("b","b1")
col.Add("a","a1")
col.Add("d","d1")
Like #Krishnadditya mentioned, Collections aren't ideal for sorting because they contain items of type Object which is too genereic to be useful in comparing against each other. If you weren't married to a collection, you can do this with a LINQ query to a list or anything that can be cast an enumerable
Dim list = {
New With {.Object = "b", .Key = "b1"},
New With {.Object = "a", .Key = "a1"},
New With {.Object = "d", .Key = "d1"}}
Dim sortedList = _
From item In list
Order By item.Key
As Collection.Add takes general object type and no specific type - sort is not possible. As objects need to be compared against each other to be sort, it will be like comparing oranges and apples.
And Collection provides sorting by Key and not value.
I think, you may have to extend the Collection class and implement the sort. you can move the items by using Insert and RemoveAt methods.
And Just a thought/advice:
if the values are of specific type, how about using some other data structure. Like dictionary for which you can sort by value as mentioned in this link
I decide to use dictionary:
Dim newcol = (From entry In col
Order By entry.Value Descending).ToDictionary(
Function(pair) pair.Key,
Function(pair) pair.Value)
Related
Can't wrap my head around this one but heres the scenario
2 Simple sample class:
Public Class Person
Public Name As String
Public Age as Integer
Public Last_Contact As New List(Of ContactInfo)
End Class
Public Class ContactInfo
Public Contact_Date As Date
Public Convo as String
End Class
in the Sub:
Dim myPersonList as New List(Of Person)
Now i know hot to sort myPersonList by first name and by age by simply doing the following:
myPersonList.Sort(Function (x,y) x.name.CompareTo(y.name))
How do I sort myPerson list by the order of the Last_Contact.ContactDate which is an already sorted list
So basically say in myPersonList I have
{Dave, 32, {02/22/2021, "Sample Convo"}, {01/12/2021 "Sample Convo"}}, John, 34 {04/08/2020, "sample convo"}}
How can I sort it so that the first item would be John would be the first item since the last contact date is the latest?
Thank you.
It appears that you want to order the Person items by their most recent Contact_Date value. In that case, you need to get a Max value from the Last_Contact collection of each Person and compare those:
Dim people As New List(Of Person)
'...
people.Sort(Function(p1, p2) p2.Last_Contact.Max(Function(ci) ci.Contact_Date).CompareTo(p1.Last_Contact.Max(Function(ci) ci.Contact_Date)))
Note that I have reversed the order of p1 and p2 in the comparison. That is because the default sort order is ascending so, if you want descending, you need to reverse the order of the items when comparing.
It's worth noting that this code will enumerate the Last_Contact collections of both items each time a comparison is made. If the amount of data is small then that's no big deal but if there's a lot of data then that might make the whole process take a significant amount of time. Here's a more verbose alternative that will save time for large data sets:
Dim people As New List(Of Person)
'...
Dim items = people.ToArray()
Dim keys = Array.ConvertAll(items, Function(p) p.Last_Contact.Max(Function(ci) ci.Contact_Date))
Array.Sort(keys, items)
people.Clear()
people.AddRange(items)
This takes advantage of the existing array functionality that will sort an array of items based on another array of key values. There's overhead involved in this method, because you're creating arrays and then clearing and repopulating the collection. As such, it would likely be less efficient for small data sets but would certainly be more efficient for larger data sets.
I have ArrayList with multi dimensional values in Structure as ArrayList
Structure
(0)=> Mstructure.ABCD
Area (these are value of ABCD)
Name
(1)=> Mstructure.EFGH
Area
Name
I want to know the index of string ABCD.
I tried
Dim myIndex as Integer = Structure.IndexOf("ABCD")
Dim myIndex = Array.IndexOf(Structure.ToArray(), myString)
This returned only -1. I want to get 0 if string is ABCD
EDITED
Structure is defined as ArrayList. I can iterate over it if I use loop e.g
Structure(i).GetType().Name = "ABCD"
I have checked if it exists in the ArrayList
Dim result = Structure.ToArray().Any(Function(x) x.ToString().Contains("ABCD"))
But I want to know the index of the multidimensional ArrayList without looping it. I want to get the index of Mstructure.ABCD. Msttructure.ABCD has values inside it but without knowing those values I want to get the index value.
(0){Mstructure.ABCD}
(1){Mstructure.EFGH}
(2){Mstructure.IJKL}
hhmmm i would use collections instead, or create an object with multi collections to make it easier for me... Have you thought of that?
Is it possible to do a for each joining two list and browse through the new "duo list" ?
something like :
For Each elm In list1 Join list2
elm.obj1 // objet with list1 type
elm.obj2 // object with list2 type
next elm
Lists have the same length.
You can join by index with LINQ's Enumerable.Zip and create an anonymous type:
Dim zipped = list1.Zip(list2, Function(obj1, obj2) New With {Key obj1, Key obj2})
For Each pair In zipped
Console.WriteLine(pair.obj1)
Console.WriteLine(pair.obj2)
Next
Note that zipped is not a collection yet but a query. Whenever you will enumerate it(f.e. with For Each, First, ToList etc.) you will execute this query again. If you need to persist it you can create a list or array(ToList/ToArray) and access this instead.
The lazy evaluation of many LINQ methods is called Deferred Execution.
If you mean that you want to enumerate a single, combined list then you would do this:
For Each item In list1.Concat(list2)
'Use item here.
Next
If what you actually mean is that you want to access the items in both lists at the same index at the same time then how would a For Each loop make sense? In that case you'd use a For loop and get both items by index:
For i = 0 To list1.Count - 1
Dim item1 = list1(i)
Dim item2 = list2(i)
'Use item1 and item2 here.
Next
Taking into account the Zip method used in the other answer and combining it with my Tuple suggestion:
For Each tpl In list1.Zip(list2, Function(a, b) AddressOf Tuple.Create)
Console.WriteLine(tpl.Item1)
Console.WriteLine(tpl.Item2)
Next
i have a list with 2 columns (clm1=StoreID and clmn2=ProductID).
i need to loop through this list and create a dictionary(StoreID , List(of ProductID))
i am using vb.net . can you please help me with the loop i have to make?
The list data is something like
StoreID ProductID
1 234
2 456
1 222
3 768
1 100
9 876
e.t.c.
I assume that your data is stored somewhat like this:
Structure Item
Public StoreID As Integer
Public ProductID As Integer
End Structure
Dim l As List(Of Item)
Then you have two options. The first one is to create the dictionary by hand:
Dim dictionary As New Dictionary(Of Integer, List(Of Integer))
For Each item As Item In l
Dim subList As List(Of Integer)
Dim keyExists = dictionary.TryGetValue(item.StoreID, subList)
If keyExists Then
subList.Add(item.ProductID)
Else
subList = New List(Of Integer)
subList.Add(item.ProductID)
dictionary.Add(item.StoreID, subList)
End If
Next
Here, you just iterate all items. Check the dictionary if it already contains an entry for the store id. If so, just add the product id. If not, create an entry and then add the product id.
If you're not overly concerned with performance, you can use the following LINQ expression to create the dictionary:
Dim dictionary = l.GroupBy(Function(item) item.StoreID) _
.ToDictionary(Function(group) group.Key, _
Function(group) group.Select(Function(item) item.ProductID) _
.ToList())
You first group the elements by their StoreID. Then, the ToDictionary() method creates the dictionary. It takes two parameters. The first one is a function that specifies the key of each element. In this case, we want to use the group's key as the dictionary key (which is the store id). The second parameter is the value that is inserted in the dictionary. First, we use Select to map each Item to its ProductID (because we want to store product ids and not entire items. Then we call ToList() to generate a list from the items in the group.
I want to create an in-memory object in VB.Net with multiple columns. What I am trying to do is create an index of some data. It will look like:
Row 1: 23 1
Row 2: 5 1
Row 3: 3 38
...
I know I can use a rectangular array to do this, but I want to be able to use indexOf opearations on this object. Is there any such structure in VB.Net?
WT
Define a row class, and then create a list of rows, like so:
Class row
Inherits Collections.ArrayList
End Class
Dim cols As New List(Of row)
Now you can access your objects using a x/y notation:
cols(0)(1)
Note this is just a simple example, your structure is uninitialized and untyped.
You can also Shadow the IndexOf function in your own class, for example finding the indexOf by an item's name:
Class col
Inherits Generic.List(Of Object)
Shadows Function IndexOf(ByVal itemName As String) As Integer
Dim e As Enumerator = Me.GetEnumerator
While e.MoveNext
If CType(e.Current, myType).name = itemName Then
Return e.Current
End If
End While
End Function
End Class
You can then access it like so:
Private cols As New col
cols.IndexOf("lookingfor")
If the number of cells in each row is constant and you don't need to grow or shrink the structure, then a simple two-dimensional array is probably the best choice, because its exposes the best possible locality characteristics. If it is not sorted, you can implement indexOf via a simple linear search.
You can do this with a Dictionary.