Sortfind Custom Objects in a List in Vbnet Custom Sortfind Objects in Vbnet Vnnet | DigitalRise

Digital Rise Homepage

Sortfind Custom Objects in a List in Vbnet Custom Sortfind Objects in Vbnet Vnnet

Oddly enough there is nothing really new here. However, most of the documentation I’ve been sifting through has all been C# related. Since I had to code this up using at work, I thought it would be helpful to have a reference available for others out there in land.

Lately I’ve really fallen in love with the power that generics, List(Of T) and the like, provide for my day to day operations. [Yes, I know this came out in .Net 2.0 but things run a little slow over here…] What I especially like to do is create my own custom objects and maintain them in a List collection for easy iteration and retrieval. Recently I created user information object that I use for somebody logged in as part of the data model. I created a List(Of UserInfo) and could add/remove/list these users without hassle. However, I also needed to sort this list from time to time. In addition, I would need to find an individual user to display particular data about them on a page. Doing this took a small amount more work, but is quick and easy once you know what to do.

UserInfo Object

For starters, here is the object I’ve created to store user information (property/method information has been removed for brevity):

? 01.Public Class UserInfo      

 02.   ‘ Private Members
03.   Private m_UserId As String
04.   Private m_FirstName As String
05.   Private m_LastName As String
06.   Private m_SecurityLevel As Integer
07.   Private m_Roles As List(Of String)
08.   ‘ Public Properties
09.   Public Property UserId() As String
10.      Get
11.         Return m_UserId
12.      End Get
13.      Set(ByVal value As String)
14.         value = m_UserId
15.      End Set
16.   End Property
17.   Public Property FirstName() As String
18.      Get
19.         Return m_FirstName
20.      End Get
21.      Set(ByVal value As String)
22.         m_FirstName = value
23.      End Set
24.   End Property
25.   Public Property LastName() As String
26.      Get
27.         Return m_LastName
28.      End Get
29.      Set(ByVal value As String)
30.         m_LastName = value
31.      End Set
32.   End Property
33.   Public Property SecurityLevel() As Integer
34.      Get
35.         Return m_SecurityLevel
36.      End Get
37.      Set(ByVal value As Integer)
38.         m_SecurityLevel = value
39.      End Set
40.   End Property
41.   Public Property Roles() As List(Of String)
42.      Get
43.         Return m_Roles
44.      End Get
45.      Set(ByVal value As List(Of String))
 46.         m_Roles = value
47.      End Set
48.   End Property
49.   ‘ Constructors
50.   Public Sub New()
 51.      m_Roles = New List(Of String)
 52.   End Sub
53.   Public Sub New(ByVal UserId As String, ByVal SecurityLevel As Integer)
 54.      m_UserId = UserId
 55.      m_SecurityLevel = SecurityLevel
56.   End Sub
57.   Public Sub New(ByVal UserId As String, ByVal Roles As List(Of String))
58.      m_UserId = UserId
59.      m_Roles = Roles
60.   End Sub
61.   Public Sub New(ByVal UserId As String, ByVal FirstName As String, _
 62.                  ByVal LastName As String, ByVal SecurityLevel As Integer, _
 63.                  ByVal Roles As List(Of String))
64.      m_UserId = UserId
65.      m_FirstName = FirstName
66.      m_LastName = LastName
67.      m_SecurityLevel = SecurityLevel
 68.      m_Roles = Roles
69.   End Sub      
70.   ‘ Public methods
71.   Public Sub AddUserRole(ByVal RoleName As String)
 72.      If Not m_Roles.Contains(RoleName) Then
73.         m_Roles.Add(RoleName)
74.      End If
75.   End Sub
76.   Public Sub RemoveUserRole(ByVal RoleName As String)
77.      If m_Roles.Contains(RoleName) Then
78.         m_Roles.Remove(RoleName)
79.      End If
80.   End Sub
81.End Class

All of this is pretty straightforward. You can easily work with a UserInfo object to modify details and add it into a List collection without a problem.

But what happens if you need to find a given user within the list, or sort the list after adding several users to it? In the past you’ve had to essentially write your own collection type object, but thanks to existing interfaces and generics, we simply have to implement two features to make this possible.

IComparer(Of T)

The IComparer interface provides an interface for comparing one object to another. With the advent of generics, we can take the comparer one step further and specify the type of item we are comparing to, which eliminates the need to additional type casting within the method itself.

Without existing UserInfo object, simply add the following line to the class declaration:

? 1.Public Class UserInfo
2.      Implements IComparer(Of UserInfo)

Then within our method, we need to implement the proper Comparer signature that the interface is looking for:

? 1.Public Function Compare(ByVal x As UserInfo, ByVal y As UserInfo) _
2.       As Integer _
3.       Implements System.Collections.Generic.IComparer(Of UserInfo).Compare
5.   Return String.Compare(x.UserId, y.UserId)
7.End Function

In our case, we are going to use the UserId as the item to sort with since we know the usernames are going to be unique. The String.Compare() method returns the proper integer comparison (-1 for less than, 0 for equal, 1 for greater than) that the compare method uses when sorting a collection. You can easily wire this method up however you’d like based on what you want to compare. You can even write up multiple comparison type operators, and then feed a given comparison into the sort method of a collection to sort according to your needs.

Predicate Methods

In order to find a given user within the collection, we leverage a feature called the Predicate to make this happen. Again, in the old way of doing things you’d have to iterate through your custom object and manually look for the item you wanted. While the predicates are essentially doing the same thing, you can define them in ways that will allow you to return more than a single item. So you can do things like find all items in the collection where the last name is like “son” or something to that effect. I’m no expert on predicates, I’m just learning to use them myself, but there is a nice MSDN article on them here.

In our case, we want two simple predicates. One to find a given UserInfo object, and one to find a given UserInfo object based on the user Id (since that is our “primary key”) without having to create the entire object. Defining these methods looks like this:

? 01.Public Shared Function FindPredicate(ByVal User As UserInfo) _
02.       As Predicate(Of UserInfo)
04.   Return Function(User2 As UserInfo) User.UserId = User2.UserId
06.End Function
08.Public Shared Function FindPredicateByUserId(ByVal User As String) _
09.       As Predicate(Of UserInfo)
11.   Return Function(User2 As UserInfo) User = User2.UserId
13.End Function

That’s all there is to it. When you’re ready to use this object with a collection, you can do simple stuff like this:

? 01.Dim UserList As New List(Of UserInfo)
02.Dim LoadUser As UserInfo
03.Dim LostUser As UserInfo
04.Dim FoundUser AS UserInfo
05.LoadUser = New UserInfo(“Sammy”, 25)
07.LoadUser = New UserInfo(“Hobart”, 50)
09.LoadUser = New UserInfo(“Fester”, 10)
11.LoadUser = New UserInfo(“Kozaky”, 25)
14.LostUser = New UserInfo(“Hobart”, 50)
15.FoundUser = UserList.Find(LostUser)
16.FoundUser = UserList.Find(UserInfo.FindPredicateByUserId _
17.                         (User.Identity.Name))

There you have it! Simple and plain and effective.