views:

2149

answers:

3

Currently I have an object implementing the IComparable interface (ASP.NET 3.5, VB). When I place several instantiated objects into a Generics list, I sort them by doing a simple someList.Sort. My CompareTo() function is this:

Public Function CompareTo(ByVal obj As Object) As Integer Implements 
System.IComparable.CompareTo
 'default is number of votes (opposite direction, highest first)'
 Dim sent As Sentence = CType(obj, Sentence)
 Return Not Points.CompareTo(sent.Points)
End Function

This works fine, except now I need to sort by another property, the DateSubmitted property, as a subset of the Points. For example, if three sentences have votes: 3, 1, 1, I want the one with the highest votes first (obviously) and of the two sentences with one vote, the one submitted the earliest to be listed.

Is this possible with CompareTo(), or should I just hit the database again and sort it there?

Thanks

+2  A: 

Since you're on .NET 3.5, you can sort using the OrderBy extension method easily:

Dim someSortedList = someList.OrderBy(Function(item) item.SomeColumn) _
                             .ThenBy(Function(item) item.SomeOtherColumn)
                             .ToList()

' OrderByDescending and ThenByDescending are also there for descending order

Whether you should hit the database again or not depends on how you've retrieved data in the first place. If you have a large data set and you had retrieved only a small subset of it from the DB, then no, you should just ask the DB to grab a small subset of data based on the new sort order. Otherwise, if you already have the whole thing in memory, just sort it as I mentioned above.

Mehrdad Afshari
i can't get someList.OrderBy to pop up in the intellisense... i'm pretty sure i'm using 3.5 (i'm using VS08), but i guess it's possible i'm not?
Jason
Do you have `Imports System.Linq`? Make sure there's a "<add namespace="System.Linq" />" in your Web.config.
Mehrdad Afshari
ah! ok... i imported Linq... what does "item" represent? is that an instantiated object?
Jason
wait, nevermind... apparently it's just whatever i want... testing...
Jason
"Function(item) item.SomeColumn" is a lambda expression. Basically you are writing an inline function that gets an item of that list as a parameter and returns the value of the column to be sorted by. "item" represents the parameter to that function. By the way, this expression is lazily evaluated. If you want to store it as a list immediately, you should add a .ToList() at the end.
Mehrdad Afshari
this is amazing. i didn't even know this existed. can you use linq on anything, or just generics, etc?
Jason
and THANK YOU!!
Jason
It's a part of LINQ. You can use any type that implements IEnumerable(Of T).
Mehrdad Afshari
You're welcome. By the way, you could also use query expression syntax if you prefer: "Dim someSortedList = From item in someList OrderBy item.SomeColumn, item.SomeOtherColumn Select item"
Mehrdad Afshari
ok so it's not working like i thought... i want to nail this down completely before i accept... it's giving me an error when i try to use it now: Unable to cast object of type 'System.Linq.OrderedEnumerable`2[myProject.Sentence,System.DateTime]' to type 'System.Collections.Generic.List`1[myProject.Sentence]'.
Jason
As I said in a comment, if you want to use it as a List, you should append a .ToList() at the end.
Mehrdad Afshari
that's the stuff... thanks again :)
Jason
+1  A: 

Like so perhaps?

Dim result as Integer = Not Points.CompareTo(sent.Points)
If result = 0 Then _
   result = Date.CompareTo(sent.Date)
Return result
Spencer Ruport
what does "result" represent? I guess I don't really understand what the "CompareTo" function yields... I kind of took it from a tutorial without understanding how it truly worked :X
Jason
CompareTo returns -1, 0, 1. -1 means "less than", 1 means "greater than" and 0 means equal. So if the points are not equal it sorts by those, if they are equal it sorts by date.
Spencer Ruport
To be more accurate, it can return any negative value (not just -1) to indicate less than and any positive value to indicate greater than.
Mehrdad Afshari
Per Mehrdad's point, it's probably more correct to negate (-) the result of Points.CompareTo rather than "Not" it.
dahlbyk
+4  A: 

Your CompareTo() function is incorrect. You need to return correct results for three states (<, =, and >), and your Not means the function only correctly handles two of them. This will cause problems if you call the function on a large enough list.

As MehrdadA already mentioned, .Net 3.5 has a simpler way to handle it. But here's how to do it if for some reason you can't handle the lambda expressions:

Public Function CompareTo(Of Sentence)(ByVal obj As Sentence) As Integer _
  Implements System.IComparable.CompareTo(Of Sentence)

    Dim result As Integer = Points.CompareTo(obj.Points) * -1
    If result = 0 Then result = DateSubmitted.CompareTo(obj.DateSubmitted)
    Return result
End Function

Note that you now want to implement IComparable(Of T), rather than IComparable.

Joel Coehoorn
+1, thank you... i went w/the linq solution, but this helped me understand CompareTo()
Jason