Generic (Multiple) Sorting For Typed Collections
posted on 2004-11-13 at 23:10:47 by Joel Ross
I ran across this post earlier in the week, and it gave me the last peice I needed to complete my sorter class I wrote earlier this spring. Mine was missing the ability to sort on any type but strings. At the time, that was fine - all properties were treated as strings. But as I moved this to another project, we needed to sort on other types, like dates, numbers, etc. Using reflection, you can dynamically find the property, and then compare the values based on the type of the property.
So how is mine different than the one linked above? Mine supports subsorting. So I could sort orders by the date they were sold (descending), then by the sales person who sold them (ascending), and then by the total amount of the order (descending). Pretty powerful.
Anyway, here's the class:
1: using System;
2: using System.Collections;
3: using System.Reflection;
4:
5:
6: namespace Ross.Utilities {
7: /// <summary>
8: /// Class used to sort objects
9: /// </summary>
10: public class Comparer : IComparer {
11: private ArrayList _sortClasses;
12:
13: /// <summary>
14: /// The collection of sorting classes
15: /// </summary>
16: public ArrayList SortClasses {
17: get { return _sortClasses; }
18: }
19:
20: /// <summary>
21: /// Default Constructor
22: /// </summary>
23: public Comparer() {
24: _sortClasses = new ArrayList();
25: }
26:
27: /// <summary>
28: /// Constructor that takes a collection of sorting classes
29: /// </summary>
30: /// <param name="SortClasses">The prebuilt collection of sort information</param>
31: public Comparer(ArrayList SortClasses) {
32: _sortClasses = SortClasses;
33: }
34:
35: /// <summary>
36: /// Constructor that takes the information about one sort
37: /// </summary>
38: /// <param name="SortColumn">The column to sort on</param>
39: /// <param name="SortDirection">The direction to sort</param>
40: public Comparer(string SortColumn, SortDirection SortDirection) {
41: _sortClasses = new ArrayList();
42: _sortClasses.Add(new SortClass(SortColumn, SortDirection));
43: }
44:
45: /// <summary>
46: /// IComparer interface implementation to compare two objects
47: /// </summary>
48: /// <param name="x">Object 1</param>
49: /// <param name="y">Object 2</param>
50: /// <returns></returns>
51: public int Compare(object x, object y) {
52: if(SortClasses.Count == 0) {
53: return 0;
54: }
55: return CheckSort(0, x, y);
56: }
57:
58: /// <summary>
59: /// Recursive function to do sorting
60: /// </summary>
61: /// <param name="SortLevel">The current level we are sorting at</param>
62: /// <param name="MyObject1">Object 1</param>
63: /// <param name="MyObject2">Object 2</param>
64: /// <returns></returns>
65: private int CheckSort(int SortLevel, object MyObject1, object MyObject2) {
66: int returnVal = 0;
67:
68: if(SortClasses.Count - 1 >= SortLevel) {
69: object valueOf1 = MyObject1.GetType().GetProperty(((SortClass) SortClasses[SortLevel]).SortColumn).GetValue(MyObject1, null);
70: object valueOf2 = MyObject2.GetType().GetProperty(((SortClass) SortClasses[SortLevel]).SortColumn).GetValue(MyObject2, null);
71:
72: if(((SortClass) SortClasses[SortLevel]).SortDirection == SortDirection.Ascending) {
73: returnVal = ((IComparable) valueOf1).CompareTo(valueOf2);
74: }
75: else {
76: returnVal = ((IComparable) valueOf2).CompareTo(valueOf1);
77: }
78:
79: if(returnVal == 0){
80: returnVal = CheckSort(SortLevel + 1, MyObject1, MyObject2);
81: }
82: }
83: return returnVal;
84: }
85: }
86:
87: /// <summary>
88: /// Enumeration to determine sorting direction
89: /// </summary>
90: public enum SortDirection {
91: /// <summary>Sort Ascending</summary>
92: Ascending = 1,
93:
94: /// <summary>Sort Descending</summary>
95: Descending = 2
96: }
97:
98: /// <summary>
99: /// Class used to hold sort information
100: /// </summary>
101: public class SortClass {
102: /// <summary>
103: /// Default constructor taking a column and a direction
104: /// </summary>
105: /// <param name="SortColumn">The column to sort on</param>
106: /// <param name="SortDirection">The direction to sort.</param>
107: public SortClass(string SortColumn, SortDirection SortDirection) {
108: this.SortColumn = SortColumn;
109: this.SortDirection = SortDirection;
110: }
111:
112: private string _sortColumn;
113:
114: /// <summary>
115: /// The column to sort on
116: /// </summary>
117: public string SortColumn {
118: get { return _sortColumn; }
119: set { _sortColumn = value; }
120: }
121:
122: private SortDirection _sortDirection;
123:
124: /// <summary>
125: /// The direction to sort
126: /// </summary>
127: public SortDirection SortDirection {
128: get { return _sortDirection; }
129: set { _sortDirection = value; }
130: }
131: }
132: }
To use it, you add a SortClass to the Comparer. You can add as many as you want. A SortClass is made up of the property name and a direction. So, for example:
1: public MyCollectionClass SortMyClass(MyCollectionClass myClass){
2: Comparer comparer = new Comparer();
3: comparer.SortClasses.Add(new SortClass("MyProperty1", SortDirection.Ascending);
4: comparer.SortClasses.Add(new SortClass("MyProperty2", SortDirection.Descending);
5: myClass.Sort(comparer);
6: return myClass;
7: }
So after the sort, myClass would be sorted by MyProperty1 (ascending), then by MyProperty2 (descending).
Categories: C#