newbie_86
newbie_86

Reputation: 4610

Sort within group with linq

I have a list of objects. I want to order by Date, and then by TransParentType and then by TransType. Then I want to group by TransParentType and within each group place a specific item (if it exists) at the bottom of the group (withdrawal: specific).

Sample Data:

Date          TransParentType   TransType

2015/05/20    Purchase      investment
2015/05/20    Redemption    withdrawal: b
2015/05/20    Redemption    zz
2015/05/20    Redemption    withdrawal: a
2015/05/20    Redemption    withdrawal: specific
2015/05/20    Redemption    withdrawal: c   
2015/05/14    Purchase      investment

Expected Sorted Data:

Date          TransParentType   TransType

2015/05/14    Purchase      investment
2015/05/20    Purchase      investment
2015/05/20    Redemption    withdrawal: a
2015/05/20    Redemption    withdrawal: b
2015/05/20    Redemption    withdrawal: c
2015/05/20    Redemption    withdrawal: specific
2015/05/20    Redemption    zz  

I'm trying to do something like this, without much success. The GroupBy does not maintain my sorted data. This is as far as i've gotten. Not sure if there is a way to move the specific item to the bottom of the group or if i have to do it manually...

results = results.OrderBy(r => r.Date).ThenBy(r=>r.TransParentType)
                             .ThenBy(r => r.TransType).ToList();

var grouped = results.GroupBy(g => g.TransParentType)... 

Upvotes: 1

Views: 1092

Answers (3)

Raphaël Althaus
Raphaël Althaus

Reputation: 60503

Well, I don't see the need of the group by from your Expected sorted Data.

I would do

results = results.OrderBy(r => r.Date)
                 .ThenBy(r=>r.TransParentType)
                 //just add an order criterion, checking if TransType == the value that you want at the end
                 //as order by a boolean returns false results first, this will put "widthdrawal: specific" at the end
                 //this will only make a difference for the elements starting with "withdrawal:"
                 .ThenBy(r => r.TransType == "widthdrawal: specific")
                 //finally, order TransType for all elements
                 .ThenBy(r => r.TransType)
                 .ToList();

EDIT :

With the new specifications given, I see something (ugly) like that

        results = results
                     //order by date
                    .OrderBy(m => m.Date)
                    //order by transParentType
                    .ThenBy(m => m.TransParentType)
                     //order by the beginning of TransType (the part which may contain "withdrawal:"
                    .ThenBy(m => m.TransType.Substring(0, Math.Min(11, m.TransType.Length)))
                    .ThenBy(m => m.TransType == "withdrawal: specific")
                    .ThenBy(m => m.TransType);

Upvotes: 1

Jon Hanna
Jon Hanna

Reputation: 113382

The output you show in your question doesn't group at all, and is simply:

results.OrderBy(r => r.TransParentType).ThenBy(r => r.Date).ThenBy(r => r.TransType == "withdrawal: specific").ThenBy(r => r.TransType);

If I try your code, I find that the GroupBy does indeed maintain the order, except for obviously having it split into two separate groups.

If GroupBy is indeed upsetting an order (though I can't see how) then you could use:

var groupedResults = results.GroupBy(r => r.Trans‎ParentType).Select(grp => grp.OrderBy(r => r.Date).ThenBy(r => r.TransType == "withdrawal: specific").ThenBy(r => r.TransType));

Which will give you an IEnumerable<OrderedEnumerable<>>, so you can foreach through each block and then foreach through those in order again, and so on. (Or an IQueryable<IOrderedQueryable<>> if done against a different linq source).

Upvotes: 0

jeremcc
jeremcc

Reputation: 8741

I believe something like this will work:

results = results
    .OrderBy(r => r.Date)
    .ThenBy(r => r.TransParentType)
    .ThenBy(r => r.TransType == "withdrawal: specific" ? 1 : 0)
    .ThenBy(r => r.TransType).ToList();

Upvotes: 1

Related Questions