Sorting using an IComparer

Posts   
 
    
Mountain
User
Posts: 21
Joined: 01-Apr-2005
# Posted on: 06-Apr-2005 16:30:50   

Why is there not a sorting method that accepts an IComparer, as in the dotnet tradition? If this is possible, it would be very helpful to us. Thanks.

Posts: 46
Joined: 07-Jan-2004
# Posted on: 06-Apr-2005 16:47:42   

Mountain wrote:

Why is there not a sorting method that accepts an IComparer, as in the dotnet tradition? If this is possible, it would be very helpful to us. Thanks.

fyi

What your talking about is a in memory sort. llbgen pro is using a database sort.

What exactly are you trying to do?

John

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 06-Apr-2005 16:54:46   

Mountain wrote:

Why is there not a sorting method that accepts an IComparer, as in the dotnet tradition? If this is possible, it would be very helpful to us. Thanks.

This is implemented in 1.0.2004.2 (now in beta), which has a more usable in-memory sort than the current Sort method in 1.0.2004.1.

Frans Bouma | Lead developer LLBLGen Pro
Mountain
User
Posts: 21
Joined: 01-Apr-2005
# Posted on: 06-Apr-2005 22:26:22   

Otis wrote:

This is implemented in 1.0.2004.2 (now in beta), which has a more usable in-memory sort than the current Sort method in 1.0.2004.1.

Thanks -- very nice.

We have IComparer methods that implement custom sorts. We couldn't seem to do the same with the existing LLBL sorts.

netLearner
User
Posts: 150
Joined: 18-Oct-2003
# Posted on: 07-Apr-2005 20:37:56   

Hi Frans, Really glad to know about the in memory sort that would be added. Can you give an example of how the code would look like for this please. I am just curious. Thanks.

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 07-Apr-2005 21:09:35   

netLearner wrote:

Hi Frans, Really glad to know about the in memory sort that would be added. Can you give an example of how the code would look like for this please. I am just curious. Thanks.

customers.Sort("CompanyName", ListSortDirection.Descending, new CaseInsensitiveComparer());

for example, which sorts all entities on the property CompanyName descending, using a case insensitive comparer simple_smile . Internally values are sorted using Array.Sort() which is a quicksort implementation, and then per value the list is rebuild using a hashtable which has per value the entities with that value.

Frans Bouma | Lead developer LLBLGen Pro
Posts: 1
Joined: 06-Jul-2007
# Posted on: 06-Jul-2007 21:09:42   

In the below example, "policies" is of type PolicyStatusCollection, a class within the CollectionClasses of the BusinessEntities namespace.

policies.Sort( e.SortExpression, ListSortDirection.Ascending, new CaseInsensitiveComparer() );

This line is giving me a compiler error of the following:

Argument '3': cannot convert from 'System.Collections.CaseInsensitiveComparer' to 'System.Collections.Generic.IComparer<object>'

What gives? I don't understand why this is failing.

.....please help!

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 10-Jul-2007 10:49:35   

Please sort via an EntityView(2) object, so either get the DefaultView object of the collection and sort that using a normal SortExpression like you'd use on the db, or create a new entityview object.

Frans Bouma | Lead developer LLBLGen Pro
DaveH
User
Posts: 6
Joined: 01-Oct-2007
# Posted on: 01-Oct-2007 17:00:27   

I have a similar question/request: I'd like to do an in-memory sort of a collection based on (prefetched) values in a related entity.

Some of the existing Sort overloads accept an IComparable, but they all require a single property (in the primary entity) to sort on.

If Sort had an overload that took only ListSortDirection and an IComparable (which gets passed the entities themselves instead of the values of a single property) then I would have the freedom to sort in any number of ways--from comparing just a single property in the primary entity, to examining multiple properties--even derived/custom properties in multiple related entities.

Today I work around this by creating a generic list of my LLBLGen Pro entity collection, then calling its Sort method (passing a delegate method):


TitleCollection titles = new TitleCollection();
titles.GetMulti(null);

List<TitleEntity> titlesSortedByPrice = new List<TitleEntity>(titles);
titlesSortedByPrice.Sort(TitleComparions.SortByPrice);

I'd like to be able to eliminate that step and just directly call my entity collection's Sort method:


TitleCollection titles = new TitleCollection();
titles.GetMulti(null);
titles.Sort(TitleComparions.SortByPrice, ListSortDirection.Ascending);

In each case TitleComparions.SortByPrice is passed two TitleEntity entities, and the compare method compares Price in an entity related to Title (and if Price matches, it compares Title). For simplicity this self-servicing example doesn't prefetch, but in practice I would.

--Dave

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 12-Oct-2007 10:57:06   

Can't you use a SortExpression for the entity view and add SortClause objects based on EntityProperty("yourpropertytosorton") ? That should work OK, also for multi-level sorts.

Frans Bouma | Lead developer LLBLGen Pro
DaveH
User
Posts: 6
Joined: 01-Oct-2007
# Posted on: 12-Oct-2007 21:51:43   

When Collection.Sort didn't work, I looked for alternatives, and I considered EntityView. Like my workaround, I didn't like that making an EntityView would duplicate my existing OrderItemCollection (I really just wanted to sort it "in place"). I also didn't like the idea of adding all the LLBLGenPro-specific code that would have been necessary to put (or refetch) the main entity and its prefetched child entity into an EntityView, and to sort it. (With the end result being an EntityView instead of my original collection of OrderItemEntity, unless I added even more code to project it back into an OrderItemCollection, and figure out how to repopulate its children.)

Hence my workaround to duplicate OrderItemCollection into a List<OrderItemEntity> so I could do a simple and clean list.Sort(anyComparer).

But the cleanest approach would have been to use the very intuitive OrderItemCollection.Sort(someComparer, SortDirection.Ascending) to sort the entity collection in place (like the other Sort methods, but with much more flexibility).

Note that my _natural instinct _ was to try OrderItemCollection.Sort( -- and sure enough the Collection classes do have a Sort method! But I was disappointed when I found that all 4 overloads required specifying a single field in the primary entity.

Offhand I can't think of a case where I'd ever need to provide a custom IComparer for a single int field (for example). But I can think of many uses for having multiple IComparer methods, if they were just able to compare the two entities themselves.

When comparing two entities, the comparer would have all kinds of flexibility: it could do a simple compare on a single field, but it could also compare multiple fields; calculate/derive values to be compared; and so on...all using straightforward entityName.Field syntax to get at the values to be compared.

Thanks, --Dave

Otis avatar
Otis
LLBLGen Pro Team
Posts: 39749
Joined: 17-Aug-2003
# Posted on: 15-Oct-2007 11:25:25   

DaveH wrote:

When Collection.Sort didn't work, I looked for alternatives, and I considered EntityView. Like my workaround, I didn't like that making an EntityView would duplicate my existing OrderItemCollection (I really just wanted to sort it "in place").

It doesn't duplicate the collection data, it just contains indices to the entities in the collection. So if you sort a collection via its view, and then foreach over the view, you simply foreach over the collection in the order stored inside the view.

I also didn't like the idea of adding all the LLBLGenPro-specific code that would have been necessary to put (or refetch) the main entity and its prefetched child entity into an EntityView, and to sort it. (With the end result being an EntityView instead of my original collection of OrderItemEntity, unless I added even more code to project it back into an OrderItemCollection, and figure out how to repopulate its children.)

If you re-create a collection from the view, you get a new collection with the old entities. the entities aren't copies, the new collection simply references the old collection. But why bother, the view is the view to look at the collection in a sorted fashion.

If you want to, you can sort the collection's contents, but that's actually slower, simply because it has to re-arrange the inner collection's references, plus every piece of code looking at the collection sees the same ordering, which perhaps isn't what you want.

Hence my workaround to duplicate OrderItemCollection into a List<OrderItemEntity> so I could do a simple and clean list.Sort(anyComparer).

But the cleanest approach would have been to use the very intuitive OrderItemCollection.Sort(someComparer, SortDirection.Ascending) to sort the entity collection in place (like the other Sort methods, but with much more flexibility).

Note that my _natural instinct _ was to try OrderItemCollection.Sort( -- and sure enough the Collection classes do have a Sort method! But I was disappointed when I found that all 4 overloads required specifying a single field in the primary entity.

That's because they're old, left overs of the IBindingList implementation on the collections, which is now on the view.

So to sort on multiple fields, a new set of methods would have to be added, which is actually rather meaningless, as looking at a collection is actually done through a view: filtering a collection, sorting a collection, please use a view for that: the original collection stays the same, so you can create multiple views on the same collection, and the entity views implement all interfaces needed to consume the collection data anyway.

Offhand I can't think of a case where I'd ever need to provide a custom IComparer for a single int field (for example). But I can think of many uses for having multiple IComparer methods, if they were just able to compare the two entities themselves.

When comparing two entities, the comparer would have all kinds of flexibility: it could do a simple compare on a single field, but it could also compare multiple fields; calculate/derive values to be compared; and so on...all using straightforward entityName.Field syntax to get at the values to be compared.

So, you're then sorting on a calculated value, which you can also expose as a property, though you place the calculation outside the entity in a comparer? Isn't it then more a way of determining a given ordering based on the logic of the comparer and not sorting which is simply placing a set of values in ascending or descending order?

Frans Bouma | Lead developer LLBLGen Pro