Xander
Xander

Reputation: 932

C# Sort List by Enum

I have got a list of Entity, which has got an enum.

public class Car
{
    public int CarId { get; set; }

    public string CarName { get; set; }

    public CarCategory CarCategory { get; set; }
}

public enum CarCategory
{
    None = 0,
    kLowRange = 1,
    kMidRange = 2,
    kHighRange = 3
}

Now I have got a list of Cars, I would like to use Comparer and run it on the enum such that all the entity having CarCategory as kMidRange and kHighRange will be sorted to first in the list.

I have tried with the answer but havent found any luck.

Thanks.

UPDATE: I kinda have got the mistake I am doing. I was looking at

var sortedList = carList
  .OrderBy(x => x.CarCategory, 
                new EnumComparer<CarCategory> {
                  CarCategory.kMidRange, 
                  CarCategory.kHighRange});

But was getting only same values. I have to add .ToList() in order to get the result.

var sortedList = carList
  .OrderBy(x => x.CarCategory, 
                new EnumComparer<CarCategory> { 
                  CarCategory.kMidRange, 
                  CarCategory.kHighRange})
  .ToList();

Will give me the expected results. My mistake!

Upvotes: 19

Views: 53103

Answers (2)

kurnakovv
kurnakovv

Reputation: 71

I want to complement Dmitry Bychenko answer

Problems

  1. If the enam numeration is not sorted
  2. If the number of the value may go beyond the boundaries of the map array
  3. If map array hasn't some numbers (when you add value to enum, but not add value to map array)

Than we get System.IndexOutOfRangeException

For example

public enum CarCategory
{
    None = 0,
    kLowRange = 30,
    kMidRange = 2000,
    kHighRange = 10,
    kAnotherRangeCarCategory = 10000,
}

Than use Dmitry code

// desired order: None should be 3d, LowRange 4th, MidRange 1st, HighRange 2nd 
 int[] map = new[] {2000, 10, 0, 30};

 var sortedList = carList
   .OrderBy(x => map[(int) (x.CarCategory)])
   .ToList();

Exception

System.IndexOutOfRangeException: 'Index was outside the bounds of the array.'

Solution

var map = new Dictionary<CarCategory, int>() {
    { CarCategory.kMidRange, 0 },
    { CarCategory.kHighRange, 1 },
    { CarCategory.None, 2 },
    { CarCategory.kLowRange, 3 }
    // without "kAnotherRangeCarCategory"
};
// Order by map, if map doesn't contains enum value, push item to end.
var sortedList = carList
   .OrderBy(x => map.GetValueOrDefault(key: x.CarCategory, defaultValue: map.Count + 1)) 
   .ToList();

This code work's correct and not throw System.IndexOutOfRangeException

P.S. You can also use strings instead enums. I talked about this in this answer

P.P.S I decided to create a small open source nuget library to solve this problem.

Installation

dotnet add package Kurnakov.PriorityOrder

Code example

using PriorityOrder;

var priorities = new List<CarCategory>()
{
    CarCategory.kMidRange,
    CarCategory.kHighRange,
    CarCategory.None,
    CarCategory.kLowRange,
    // without "kAnotherRangeCarCategory"
};
var sortedList = carList
   .OrderByPriority(x => x.CarCategory, priorities) // or with params[]
   .ToList();

More details you can find here

Upvotes: 2

Dmitrii Bychenko
Dmitrii Bychenko

Reputation: 186668

enum is effectively integer (int in your case)

The approved types for an enum are byte, sbyte, short, ushort, int, uint, long, or ulong.

see https://msdn.microsoft.com/en-us/library/sbbt4032.aspx for details.

Since CarCategory implementation has the items in the desired order you can put just

 var sortedList = carList
   .OrderByDescending(x => (int) (x.CarCategory))
   .ToList();

please, note Descending: you want kHighRange to be on the top. If you want an arbitrary order, e.g.

kMidRange, kHighRange, None, kLowRange

I suggest using mapping:

 // desired order: None should be 3d, LowRange 4th, MidRange 1st, HighRange 2nd 
 int[] map = new[] {3, 4, 1, 2};

 var sortedList = carList
   .OrderBy(x => map[(int) (x.CarCategory)])
   .ToList();

Upvotes: 50

Related Questions