2

I have a List of type Test which has 4 properties and the List needs to be sorted with some specific conditions. Below are the properties of class Test and also sample data.

class Test
{
    int order;
    string value;
    string dept;
    //..... and some others
}

Sample json:

[
   {
      "order":3,
      "value":"ABC",
      "dept":"A"
   },
   {
      "order":2,
      "value":"XYZ",
      "dept":"A"
   },
   {
      "order":1,
      "value":"ABC2",
      "dept":"P"
   },
   {
      "order":4,
      "value":"XYZ2",
      "dept":"P"
   },
   {
      "order":6,
      "value":"ABC3",
      "dept":"Z"
   },
   {
      "order":5,
      "value":"XYZ3",
      "dept":"Z"
   },
]

The above json data is loaded to one List<Test>.

My requirement is to sort the above list like first the items with dept=P, then dept=A and then dept=Z and the second sort criteria is order.

I tried with OrderBy(x=>x.dept).ThenBy(x=>x.order) but the output is not what is expected.

Is there any way to specify the dept which should appear first in the list.

As a workaround I split the List into multiple lists and then merge them after sorting, but this is not the best solution I guess.

Do we have any other better and optimized solution for this?

Tufan Chand
  • 692
  • 4
  • 19
  • 36
  • 1
    There's an override of `Enumerable.OrderBy` that takes an `IComparer comparer` as its second parameter. You could write your own `DeptComparer : IComparer` that would handle your desired `P>A>Z` order. See [this thread](https://stackoverflow.com/questions/985657/use-own-icomparert-with-linq-orderby) and [the docs on OrderBy](https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.orderby?view=netframework-4.8) for more information. – pappbence96 Aug 27 '19 at 09:52
  • Ohh! Thank you so much @pappbence96 for pointing this out I will just look into it now. – Tufan Chand Aug 27 '19 at 09:56

3 Answers3

7

Well, you could create a list with your sorting rules:

var orderOfDepts = new List<string> { "P", "A", "Z" };

And use index of elements in that list for sorting:

var sortedList = myList.OrderBy(x=> orderOfDepts.IndexOf(x.dept)).ThenBy(x=> x.order).ToList();

P.S. This solution is good if sortedList collection is not too big, but if it is large or you have many sorting rules in orderOfDepts list then you might want to reduce the overall complexity of this algorithm from > O(N2) to something close to O(N*logN).

For that we can utilize Dictionarys fast lookups:

int o;
var orderOfDepts = new Dictionary<string, int> 
{
   { "P", 0 },
   { "A", 1 },
   { "Z", 2 }
};

var sortedList = myList.OrderBy(x => orderOfDepts.TryGetValue(x.dept, out o) ? o : int.MaxValue)
                       .ThenBy(x=> x.order)
                       .ToList();

Here we try to get element from the dictionary by key x.dept. If we don't find anything we put the item to the end of the list otherwise we use the value from the dictionary for sorting.

Dictionary's lookup is O(1) so it will greatly improve performance in the expense of the time that is required in order to construct the dictionary object. For few elements it would be inadvisable to do it and first solution is better but for large amounts of data this solution is good.

Fabjan
  • 13,506
  • 4
  • 25
  • 52
0

Try this:

var orderOfDepts = new List<string> { "P", "A", "Z" };

var sortedList =
(
    from x in myList
    join dept in orderOfDepts.Select((name, index) => new { name, index }) on x.dept equals dept.name
    orderby dept.index, x.order
    select x
).ToList();

It should be fairly efficient.

Enigmativity
  • 113,464
  • 11
  • 89
  • 172
-1

You can do it this way

var result = _context.OrderBy(p => new { p.dept, p.order}).ToList();