Page 1 of 1

Sorting Objects by a Property, e.g. Salary Rate Topic: -----

#1 andrewsw  Icon User is online

  • bin deployable
  • member icon

Reputation: 6284
  • View blog
  • Posts: 25,149
  • Joined: 12-December 12

Posted 27 August 2016 - 02:48 PM

How to sort staff members by their salaries. First, though, let's discuss how we store information about staff members, or any other set of associated values (attributes).

When you need to store sets of values, such as staff member names and their salaries, you should define a class to encapsulate these values. VB.NET is an object-oriented language, associated values belong in a class. It could be as simple as:

    Public Class StaffMember
        Property Name As String
        Property Salary As Decimal
    End Class


What you should not do is to work with parallel arrays. That is, to have one array holding all the names and another holding all the salaries. Such a structure is

  • Clumsy
  • Very error prone
  • Hard to maintain

It is very easy to refer to Bob but to identify Ted's salary. Sorting such a clumsy structure while maintaining the correspondence between elements is a particular challenge!

Create a Console Application. Define the StaffMember class, and a List(Of StaffMember) to maintain a staff list, then add a few staff members to this list. Here's the code:

Module Module1

    Public Class StaffMember
        Property Name As String
        Property Salary As Decimal
    End Class

    Sub Main()
        Dim staffList As New List(Of StaffMember)

        staffList.Add(New StaffMember With {.Name = "Ted", .Salary = 22000})
        staffList.Add(New StaffMember With {.Name = "Mary", .Salary = 21000})
        staffList.Add(New StaffMember With {.Name = "Fred", .Salary = 28000})
        staffList.Add(New StaffMember With {.Name = "John", .Salary = 24000})
        staffList.Add(New StaffMember With {.Name = "Elizabeth", .Salary = 21000})

        For Each member As StaffMember In staffList
            Console.WriteLine("{0} {1:c0}", member.Name, member.Salary)
        Next

        Console.ReadKey()
    End Sub

End Module


We have also printed out the staff list to confirm that it is populated.

If we attempt to sort our list using staffList.Sort() it doesn't work, with the error:

Additional information: Failed to compare two elements in the array.

There is no intrinsic method for sorting objects, particularly objects that we have just defined/invented (and Microsoft won't assume that we want to sort by Salary).

We can provide the Sort method with a lambda function:

        ' ..
        staffList.Add(New StaffMember With {.Name = "Elizabeth", .Salary = 21000})

        staffList.Sort(Function (x, y) x.Salary.CompareTo(y.Salary))

        For Each member As StaffMember In staffList
            Console.WriteLine("{0} {1:c0}", member.Name, member.Salary)
        Next


Lambda Expressions :MSDN

In order to perform a sort, pairs of values need to be compared, identified as x and y in the lambda. Each comparison needs to result in an integer value of -1, 0 or 1. -1 says that the first compared value is less than the second, 0 means they are equal, 1 means the first is less than the second. (Note that for a single expression/line lambda there is no Return statement, it is implicit. "The value returned by the single-line function is the value of the expression in the body of the function.")

Primitive types, including primitive structures such as DateTime, have a CompareTo method that obtains the value (-1, 0 or 1) that we need, so we don't have to write the logic ourselves, although we could if we needed to.

Decimal.CompareTo Method (Decimal) :MSDN

If we wanted a descending order sort we can multiply the value by -1 (so that 1 becomes -1, etc.):

        'for descending:
        staffList.Sort(Function(x, y) -1 * x.Salary.CompareTo(y.Salary))


Alternatively, we can use one of the enumerable extension methods OrderBy or OrderByDescending:

        staffList = staffList.OrderBy(Function(x) x.Salary).ToList
        'or OrderByDescending


In this case x is the object instance and we simply identify the property (or field) that we wish to order by. (A comparison method is still used but we don't have to identify it explicity.)

Using lambdas is fine if we will only need to sort occasionally. If the ability to sort should be part of the definition of our class then we can implement the IComparable interface.

    Public Class StaffMember : Implements IComparable
        Property Name As String
        Property Salary As Decimal

        Function CompareTo(ByVal obj As Object) As Integer Implements IComparable.CompareTo
            Dim member As StaffMember = CType(obj, StaffMember)
            Return Decimal.Compare(Me.Salary, member.Salary)
        End Function
    End Class


The interface requires that we provide a CompareTo method. The code for this method is very similar to what we achieved using lambdas, except that we are comparing the current instance of our object to a supplied object instance.

Here, using Decimal.Compare rather than CompareTo is less confusing, and avoids any potential clashes with our method name.

With this interface implemented it will be used by default with the Sort() method:

        staffList.Sort()        'using our comparer


Descending order can be achieved by reversing the results:

        staffList.Sort()        'using comparer
        staffList.Reverse()


If descending order should be the default then we could adjust our CompareTo method to achieve this, essentially by multiplying by -1 as we did earlier.

How to use the IComparable and the IComparer interfaces :MSDN

MSDN said:

The IComparer interface provides additional comparison mechanisms. For example, you may want to order your class on several fields or properties, by ascending and descending order on the same field, or both.

If you pursue IComparer then you could, for example, provide one comparer that results in sorting by Name and another that sorts by Salary.

So, if you just need to quickly sort a list of your objects by a property, you can use a lambda; otherwise, if sorting should be a feature of your class, consider defining it as part of your class.

Here's the full code with some parts commented out. Uncomment the parts that you want to test.

Spoiler


Is This A Good Question/Topic? 3
  • +

Replies To: Sorting Objects by a Property, e.g. Salary

#2 Michael26  Icon User is offline

  • Futurama: Insert funny joke here
  • member icon

Reputation: 414
  • View blog
  • Posts: 1,664
  • Joined: 08-April 09

Posted 29 August 2016 - 03:15 AM

There is an excellent tutorial about moving away from parallel arrays
Was This Post Helpful? 1
  • +
  • -

Page 1 of 1