mHelpMe
mHelpMe

Reputation: 6668

List custom sorting

I want to sort a custom list I have on a certain criteria. Every item in my list contains a property called "Status" which is a enumeration, shown below.

 Empty = 0, Normal = 1, Aged = 2, Dead = 3 

The values assigned above cannot be changed. When I sort on my Status property I would like the order to be as such Normal, Aged, Empty & Dead. I do not know how best to go about this?

Below is an example of a class I'm using for a different sorting issue. Not sure how I would 'convert' this class to solve my issue above?

public class SortOrders : IComparer<OrderBlocks.Order>
{
    private bool _sortDescending;

    public SortOrders(bool sortDescending)
    {
        this._sortDescending = sortDescending;
    }

    public SortOrders()
        : this(false)  // sort ascending by default
    {

    }

    public int Compare(OrderBlocks.Order x, OrderBlocks.Order y)
    {
        if (this._sortDescending)
        {
            return y.StatusGood.CompareTo(x.StatusGood);
        }
        else
        {
            return x.StatusGood.CompareTo(y.StatusGood);
        }
    }
}

Upvotes: 2

Views: 214

Answers (4)

mungflesh
mungflesh

Reputation: 786

I would create a conversion function that StatusGood is passed through prior to comparing it, ie:

public static class StatusGoodExtensions
{
    public static int OrderIndex(this StatusGood statusIn)
    {
        switch ( statusIn )
        {
            case StatusGood.Normal: return 0;
            case StatusGood.Aged: return 1;
            case StatusGood.Empty: return 2;
            case StatusGood.Dead: return 3;
        }
        throw new NotImplementedException(statusIn.ToString());
    }
}

using in the comparison, like so:

return x.StatusGood.OrderIndex().CompareTo(y.StatusGood.OrderIndex());

By having an extension method, the logic to return the order is cleanly separated from the sorting, and could be tested or re-used elsewhere.

Upvotes: 1

Cyril Gandon
Cyril Gandon

Reputation: 17048

The order by linq extension take a Func<TSource, TKey> keySelector, so you can pass a custom ordered method that return an int value base on the order you need:

public enum Status { Empty = 0, Normal = 1, Aged = 2, Dead = 3 }
public class Item
{
    public Status Status { get; set; }
    public int OrderedStatus
    {
        get
        {
            switch (this.Status)
            {
                case Status.Normal: return 0;
                case Status.Aged: return 1;
                case Status.Empty: return 2;
                default: return 3; // case Status.Dead
            }
        }
    }
    public static IEnumerable<Item> OrderByStatus(IEnumerable<Item> items)
    {
        return items.OrderBy(item => item.OrderedStatus);
    }
}

Upvotes: 1

DGibbs
DGibbs

Reputation: 14608

There are lots ways to do it using OrderBy:

Chaining OrderBy and ThenBy calls together with your custom order:

var ordered = list.OrderBy(f1 => f1.Status == 3)
                  .ThenBy(f2 => f2.Status == 0)
                  .ThenBy(f3 => f3.Status == 2)
                  .ThenBy(f4 => f4.Status == 1).ToList();

Or use a delegate switch/case inline:

var ordered2 = list.OrderBy(foo =>
    {
       switch (foo.Status)
       {
              case (int)Status.Normal:
                    return 0;
              case (int)Status.Aged:
                    return 1;
              case (int)Status.Empty:
                    return 2;
              case (int)Status.Dead:
                    return 3;
              default:
                    return 0;
       }
    }).ToList();

Both give the same results. The first method uses the enum values you already have, the second looks at the enum value and returns a different integer to be used for comparison.

Upvotes: 0

nima
nima

Reputation: 6733

Here's how I would do it using Linq:

        var sorted = myList.OrderBy(x =>
                                    {
                                        switch (x.Status)
                                        {
                                            case SomethingStatus.Normal:
                                                return 0;
                                            case SomethingStatus.Aged:
                                                return 1;
                                            case SomethingStatus.Empty:
                                                return 2;
                                            case SomethingStatus.Dead:
                                                return 3;
                                            default:
                                                return 10;
                                        }
                                    });

Upvotes: 2

Related Questions