I’ve been writing IComparer’s for a while, and could never figure out a "clean" way to write one which handled mutliple values to compare against.
One time I tried stringing together the multi values, but that was deficient fairly quickly, when you went to datatypes outside of strings.
( If you sort the numbers 1-15 with a string, you get : 1,10,11,12,13,14,15,2,3,4,5,6,7,8,9, which is not what you want). Throw some DateTime’s in the mix, and you see that can get ugly very quickly.
Well, no longer. The following sample shows how to have your cake and eat it too.
The EmployeeComparer can handle strings, times, ints (or others). It can handle 1:N number of sort parameters, where N is probably the overall number of properties you have on your object.
And it only does the work needed, aka, it doesn’t run 5 comparing operations when 1 will suffice.
The little trick is to nest some comparisons, in case there is a "tie" ( compare value of 0 ).
The other good news is that this solution is not limited to 1, 2 or 3 levels of comparing values.
The limit is your imagination.
However, the solution does not do unnecessary compares either.
I think the solution is pretty elegant.
The example can sort ASC or DESC. However, it sorts ASC and DESC for the entire set of SortColumns.
I’m working on my own solution for this. (Just a wrapper object to hold the SortColumn and the direction).
But I will leave that as an exercise for the reader.
Here is the downloadable example. (Right Click and "Save Target As" is your best bet). This is 1.1 code, fyi.
Here\’s a generic class I wrote which does the same sort of thing. You might find it useful in your 2.0 projects:
public class AnythingComparer<T> : System.Collections.Generic.IComparer<T>{
/// <summary>/// Sorts/Compares any type of object by any number of Properties/// </summary>/// <param name="property">Property name followed by sort direction. e.g. \’MyProperty DESC\'</param>
public AnythingComparer(string property){ _properties = new string[] { property };}
/// <summary>/// Sorts/Compares any type of object by any number of Properties/// </summary>/// <param name="properties">An array of property names followed and sort direction. e.g. {\’MyProperty DESC\’, \’Property2 ASC\’}</param>
public AnythingComparer(string[] properties){ _properties = properties;}
private string[] _properties;
/// <summary>/// An array of property names followed and sort direction. e.g. {\’MyProperty DESC\’, \’Property2 ASC\’}/// </summary>public string[] Properties{ get { return _properties; } set { _properties = value; }}
public int Compare(T x, T y){ if (x.GetType() != y.GetType()) throw new ArgumentException("Can\’t compare " + x.GetType() + " with " + y.GetType());
int res = 0; for (int i = 0; i < _properties.Length; i++) { string[] args = _properties[i].Split(\’ \’); string prop = args[0]; bool desc = args.Length > 0 && args[1].Equals("DESC", StringComparison.OrdinalIgnoreCase); object o1 = x.GetType().GetProperty(prop).GetValue(x, null); object o2 = y.GetType().GetProperty(prop).GetValue(y, null); if (o1 == null) throw new NullReferenceException(); if (o2 == null) throw new NullReferenceException();
IComparable comp1 = o1 as IComparable; IComparable comp2 = o2 as IComparable;
if (comp1 == null) throw new ArgumentException("Property \’" + prop + "\’ of type " + o1.GetType() + " is not IComparable"); if (comp2 == null) throw new ArgumentException("Property \’" + prop + "\’ of type " + o2.GetType() + " is not IComparable"); res = comp1.CompareTo(o2) * (desc ? -1 : 1); if (res != 0) return res; } return res; }}
… usage would be something like:
List<SomeClass> ar = new List<SomeClass>();… populate the array …AnythingComparer<SomeClass> comp = new AnythingComparer<SomeClass>(new string[] { "SomeProp ASC", "SomeBool DESC", "SomeDate ASC", "SomeEnum ASC" });ar.Sort(comp);
I wanted to let you know that the AnythingComparer is awesome and works very well. We came across a small issue with property names in different cases (camel case vs upper case). So I added a check for this and though that i\’d post it back.
Imports SystemImports System.Collections.GenericImports System.Text
<Serializable()> Public Class AnythingComparer(Of T) Implements IComparer(Of T)
\’\’\’ <summary> \’\’\’ Sorts/Compares any type of object by any number of Properties \’\’\’ </summary> \’\’\’ <param name="property">Property name followed by sort direction. e.g. \’MyProperty DESC\'</param>
Public Sub New(ByVal [property] As String) _properties = New String() {[property]} End Sub
\’\’\’ <summary> \’\’\’ Sorts/Compares any type of object by any number of Properties \’\’\’ </summary> \’\’\’ <param name="properties">An array of property names followed and sort direction. e.g. {\’MyProperty DESC\’, \’Property2 ASC\’}</param>
Public Sub New(ByVal properties As String()) _properties = properties End Sub
Private _properties As String()
\’\’\’ <summary> \’\’\’ An array of property names followed and sort direction. e.g. {\’MyProperty DESC\’, \’Property2 ASC\’} \’\’\’ </summary> Public Property Properties() As String() Get Return _properties End Get Set(ByVal value As String()) _properties = value End Set End Property
Public Function Compare(ByVal x As T, ByVal y As T) As Integer Implements System.Collections.Generic.IComparer(Of T).Compare If Not x.GetType() Is y.GetType() Then Throw New ArgumentException("Cannot compare " & x.GetType().ToString() & " with " & y.GetType().ToString()) End If
Dim res As Integer = 0 For i As Integer = 0 To _properties.Length – 1 Dim args As String() = _properties(i).Split(" "c) Dim prop As String = args(0) Dim desc As Boolean = args.Length > 0 AndAlso args(1).Equals("DESC", StringComparison.OrdinalIgnoreCase)
\’ Updated here
\’ get all properties for x/y (should be the same for same object type) Dim props As System.Reflection.PropertyInfo() = x.GetType().GetProperties()
Dim j As Integer For j = 0 To props.Length If props(j).Name.ToLower() = args(0).ToLower() Then prop = props(j).Name Exit For End If Next
\’ End Update
Dim o1 As Object = x.[GetType]().GetProperty(prop).GetValue(x, Nothing) Dim o2 As Object = y.[GetType]().GetProperty(prop).GetValue(y, Nothing)
If o1 Is Nothing Then Throw New NullReferenceException() End If If o2 Is Nothing Then Throw New NullReferenceException() End If
Dim comp1 As IComparable = TryCast(o1, IComparable) Dim comp2 As IComparable = TryCast(o2, IComparable)
If comp1 Is Nothing Then Throw New ArgumentException("Property \’" & prop & "\’ of type " & o1.GetType().ToString() & " is not IComparable") End If If comp2 Is Nothing Then Throw New ArgumentException("Property \’" & prop & "\’ of type " & o2.GetType().ToString() & " is not IComparable") End If
res = comp1.CompareTo(o2) * (IIf(desc, -1, 1))
If res <> 0 Then Return res
End If Next Return res End Function
End Class
It would be used the same way as noted above.
Thanks again!
wow gold wow gold wow gold wow power leveling wow power leveling wow power leveling wow power leveling World of Warcraft Gold wow gold wow power leveling wow gold wow gold wow gold wow power leveling wow power leveling Rolex Replica rolex Rolex Replica rolex Rolex changyongkuivip
Excellent, thank you!!
Thank you very much