Jonny Cundall
Jonny Cundall

Reputation: 2612

is there a better way to order a IEnumerable to match an arbitrary ordering?

I came across this problem at work, and though I have a solution, I can't help feeling there is a more elegant way. The use of List.IndexOf() stands out as a bit hacky to me.

I have to sort a collection of BreakdownItems by credit rating. Credit ratings don't follow alphabetical order so I've treated them as just having some arbitrary, non logical order.

IEnumerable<BreakdownItem> unsortedCreditRatings = new List<BreakdownItem>
        {
            new BreakdownItem{ Name = "CCC", Weight=20d},
            new BreakdownItem{ Name = "AA", Weight=20d},
            new BreakdownItem{ Name = "AAA", Weight=10d},
            new BreakdownItem{ Name = "B", Weight=50d},
        };

        var sortOrder = new List<string> 
    { "AAA", "AA", "A", "BBB", "BB", "B", "CCC", "below CCC" };

        var sortedRatingBreakdown = unsortedCreditRatings
           .OrderBy(item => sortOrder.IndexOf(item.Name));

Upvotes: 3

Views: 209

Answers (3)

Anthony Pegram
Anthony Pegram

Reputation: 126854

Enumerable.Join preserves the order of the first (or outer) sequence. If you are not keen on the enum approach, you can use this and without needing to do OrderBy explicitly.

var orderedRatings = from item in sortOrder
                     join rating in unsortedCreditRatings
                     on item equals rating.Name
                     select rating;

Upvotes: 2

James Michael Hare
James Michael Hare

Reputation: 38397

As I alluded in my comment above, having the multiple credit ratings as a string can cause data integrity issues, I'd move those to an enum instead, such as:

public enum CreditRatings
{
     AAA,
     AA,
     A,
     BBB,
     BB,
     B,
     CCC,
     etc
}

And you instead store that in your BreakdownItem, you can do:

var sortedRatingBreakdown = unsortedCreditRatings.OrderBy(item => item.Rating);

If you must store them as a string, and can't use an enum you could consider using a Dictionary<string, int> or something like that to store your ordering so that you at least get O(1) lookup time:

var ratingOrders = new Dictionary<string,int>
    {
        { "AAA", 1 },
        { "AA", 2 },
        { "A", 3 },
        etc...
    };

Then you can order by the results of the dictionary:

var sortedRatingBreakdown = unsortedCreditRatings.OrderBy(item => ratingOrders[item.Name]);

Upvotes: 3

cadrell0
cadrell0

Reputation: 17307

Can you make the credit rating an enum instead of a string? You could then assign those enum values the correct sort order.

Upvotes: 5

Related Questions