Wednesday, September 13, 2006

(not?) Ranting about .NET collections...

The .NET collections continually frustrate me with the obvious ommisions, even in .NET 2.0. Coming from a Java / Lisp background, I really expect two things out of a data structures API:

  • Lots of collections to choose from.

  • and; Easy manipulation of the structures you have available.


.NET doesn't fill either of these requirements very well. At least we have generics now (which, admittedly, is a step above what's available in Lisp -- with regard to types, anyway).

Today I ran into (yet another) annoyance with .NET collections -- sorting arrays elegantly. Given an array, you can sort it in accending order (according to the default comparer) with Arrays.Sort(..).


// build & populate the array.
double[] values = source.ToArray();

// destructively!! sorts values, returns void, of course.
Array.Sort(values);


That's nice. Now, sort it in reverse:


// build & populate the array.
double[] values = source.ToArray();

Array.Sort(values);
// reverse the array.. adds O(n) ops.
Array.Reverse(values);


or...


// build & populate the array.
double[] values = source.ToArray();

Array.Sort(values, Double.ReverseComparer);


Oh, wait. There is no ReverseComparer on Double.. actually, there's no Comparer on Double either, but there is for most objects... so in general I could just wrap the comparer in a delegate or an anon class (to invert it) and use that.

Wait again.. c# doesn't have anon classes, and Sort doesn't take a delegate under any incantation. So, we could do this:


private class ReverseDoubleComparer : IComparer<double>{
public int Compare(double x, double y){
return y.CompareTo(x);
}
}

/*
intervening code...
*/
// build & populate the array.
double[] values = source.ToArray();

Array.Sort(values, new ReverseDoubleComparer());


That will work, but wow... for every type, I'll need to create a new class, and I can only deal with classes that implement IComparable.CompareTo(..). Thankfully, I can use generics and some constructor overloading to deal with both situations:


public class BackwardsComparer<T> : IComparer<T>{
public BackwardsComparer(IComparer<T> c){
_comparer = c;
}

public int Compare(T x, T y){
return _comparer.Compare(y,x);
}

private IComparer<T> _comparer = null;
}


Now, we just need to do the following:


string[] strs = strSource.ToArray();

// sort strs in reverse alphabetical order:
Array.Sort(strs,
new BackwardsComparer<T>(StringComparer.CurrentCulture));


And there we have it -- reverse array sorting without the additional cost of a Reverse() call, and avoiding the ugliness of case-specific classes floating around. (The complete listing for BackwardsComparer and test suite are here: BackwardsComparer.cs and BackwardsComparerTest.cs