91

Similar to List<> OrderBy Alphabetical Order, we want to sort by one element, then another. we want to achieve the functional equivalent of

SELECT * from Table ORDER BY x, y 

We have a class that contains a number of sorting functions, and we have no issues sorting by one element.
For example:

public class MyClass { public int x; public int y; } List<MyClass> MyList; public void SortList() { MyList.Sort( MySortingFunction ); } 

And we have the following in the list:

Unsorted Sorted(x) Desired --------- --------- --------- ID x y ID x y ID x y [0] 0 1 [2] 0 2 [0] 0 1 [1] 1 1 [0] 0 1 [2] 0 2 [2] 0 2 [1] 1 1 [1] 1 1 [3] 1 2 [3] 1 2 [3] 1 2 

Stable sort would be preferable, but not required. Solution that works for .Net 2.0 is welcome.

4
  • @Bolu I've explicitly removed the tag to make post version agnostic and updated answers to match that. Consider making a clarifying edit in the question instead of restoring the tag if you think 4.0/2.0 was not prominent enough. Commented Oct 3, 2014 at 15:49
  • Sorry @AlexeiLevenkov, didn't pay much attention, please feel free to roll-back. Commented Oct 3, 2014 at 15:56
  • OK. Reverted the change. Commented Oct 3, 2014 at 16:07
  • This question was updated to cover all versions of .Net from original just 2.0 - contains several alternative answers for different frameworks and requirements - check out all to see which one fits your requirements better. Commented Oct 3, 2014 at 19:42

5 Answers 5

158

For versions of .Net where you can use LINQ OrderBy and ThenBy (or ThenByDescending if needed):

using System.Linq; .... List<SomeClass>() a; List<SomeClass> b = a.OrderBy(x => x.x).ThenBy(x => x.y).ToList(); 

Note: for .Net 2.0 (or if you can't use LINQ) see Hans Passant answer to this question.

Sign up to request clarification or add additional context in comments.

1 Comment

From another answer post by phoog here: stackoverflow.com/questions/9285426/… It creates another list with the original items in a new order. This is only useful if you need to preserve the original ordering for some other purpose; it's rather more wasteful of memory than sorting the list in place
99

Do keep in mind that you don't need a stable sort if you compare all members. The 2.0 solution, as requested, can look like this:

 public void SortList() { MyList.Sort(delegate(MyClass a, MyClass b) { int xdiff = a.x.CompareTo(b.x); if (xdiff != 0) return xdiff; else return a.y.CompareTo(b.y); }); } 

Do note that this 2.0 solution is still preferable over the popular 3.5 Linq solution, it performs an in-place sort and does not have the O(n) storage requirement of the Linq approach. Unless you prefer the original List object to be untouched of course.

Comments

5

The trick is to implement a stable sort. I've created a Widget class that can contain your test data:

public class Widget : IComparable { int x; int y; public int X { get { return x; } set { x = value; } } public int Y { get { return y; } set { y = value; } } public Widget(int argx, int argy) { x = argx; y = argy; } public int CompareTo(object obj) { int result = 1; if (obj != null && obj is Widget) { Widget w = obj as Widget; result = this.X.CompareTo(w.X); } return result; } static public int Compare(Widget x, Widget y) { int result = 1; if (x != null && y != null) { result = x.CompareTo(y); } return result; } } 

I implemented IComparable, so it can be unstably sorted by List.Sort().

However, I also implemented the static method Compare, which can be passed as a delegate to a search method.

I borrowed this insertion sort method from C# 411:

 public static void InsertionSort<T>(IList<T> list, Comparison<T> comparison) { int count = list.Count; for (int j = 1; j < count; j++) { T key = list[j]; int i = j - 1; for (; i >= 0 && comparison(list[i], key) > 0; i--) { list[i + 1] = list[i]; } list[i + 1] = key; } } 

You would put this in the sort helpers class that you mentioned in your question.

Now, to use it:

 static void Main(string[] args) { List<Widget> widgets = new List<Widget>(); widgets.Add(new Widget(0, 1)); widgets.Add(new Widget(1, 1)); widgets.Add(new Widget(0, 2)); widgets.Add(new Widget(1, 2)); InsertionSort<Widget>(widgets, Widget.Compare); foreach (Widget w in widgets) { Console.WriteLine(w.X + ":" + w.Y); } } 

And it outputs:

0:1 0:2 1:1 1:2 Press any key to continue . . . 

This could probably be cleaned up with some anonymous delegates, but I'll leave that up to you.

EDIT: And NoBugz demonstrates the power of anonymous methods...so, consider mine more oldschool :P

Comments

2

This may help you, How to Sort C# Generic List

Comments

1

I had an issue where OrderBy and ThenBy did not give me the desired result (or I just didn't know how to use them correctly).

I went with a list.Sort solution something like this.

 var data = (from o in database.Orders Where o.ClientId.Equals(clientId) select new { OrderId = o.id, OrderDate = o.orderDate, OrderBoolean = (SomeClass.SomeFunction(o.orderBoolean) ? 1 : 0) }); data.Sort((o1, o2) => (o2.OrderBoolean.CompareTo(o1.OrderBoolean) != 0 o2.OrderBoolean.CompareTo(o1.OrderBoolean) : o1.OrderDate.Value.CompareTo(o2.OrderDate.Value))); 

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.