The blog of flying mind

March 25, 2008

Natural Sorting in C#

Filed under: Software

Also see: Debugging an InvalidCastException

Jeff Atwood recently posted about natural sorting. This is all about making sure that strings that contain numbers sort numerically. I’m slightly surprised to see that he wants to call it alphabetical sorting. Surely by definition, alphabetical sorting is defined by, well, the alphabet. This is an issue about numbers, not letters.

Anyway, he says he tried and gave up on a succinct C# version. He suggests that it will take 40+ lines of code. I believe that’s misleading, because as far as I can tell, the Python versions are only able to be so succinct because Python already appears to know how to sort an array. Both examples he shows rely on this. In.NET, collections aren’t intrinsically sortable. Let’s sort that:

/// <summary>
/// Compares two sequences.
/// </summary>
/// <typeparam name=”T”>Type of item in the sequences.</typeparam>
/// <remarks>
/// Compares elements from the two input sequences in turn. If we
/// run out of list before finding unequal elements, then the shorter
/// list is deemed to be the lesser list.
/// </remarks>
public class EnumerableComparer<T> : IComparer<IEnumerable<T>>
{
 /// <summary>
 /// Create a sequence comparer using the default comparer for T.
 /// </summary>
 public EnumerableComparer()
 {
 comp = Comparer<T>.Default;
 }
	
 /// <summary>
 /// Create a sequence comparer, using the specified item comparer
 /// for T.
 /// </summary>
 /// <param name=”comparer”>Comparer for comparing each pair of
 /// items from the sequences.</param>
 public EnumerableComparer(IComparer<T> comparer)
 {
 comp = comparer;
 }
	
 /// <summary>
 /// Object used for comparing each element.
 /// </summary>
 private IComparer<T> comp;
	
 /// <summary>
 /// Compare two sequences of T.
 /// </summary>
 /// <param name=”x”>First sequence.</param>
 /// <param name=”y”>Second sequence.</param>
 public int Compare(IEnumerable<T> x, IEnumerable<T> y)
 {
 using (IEnumerator<T> leftIt = x.GetEnumerator())
 using (IEnumerator<T> rightIt = y.GetEnumerator())
 {
 while (true)
 {
 bool left = leftIt.MoveNext();
 bool right = rightIt.MoveNext();
	
 if (!(left || right)) return 0;
	
 if (!left) return -1;
 if (!right) return 1;
	
 int itemResult = comp.Compare(leftIt.Current, rightIt.Current);
 if (itemResult != 0) return itemResult;
 }
 }
 }
}
	

(Note: I offer the code samples on this page under the MIT license.)

So yes, I need a lot of code. However, that’s a utility class that is applicable to a wide range of scenarios, not just this one. It’s slightly irritating that it’s not already built into the.NET framework. Heck, maybe it is, and I’ve just been looking in the wrong place.

Also see: Link Love: 09/21/2007

Also see: Sometimes, it’s the small things..

Also see: VS.NET Macro To Group and Sort Your Using Statements

Also see: Link Love: 09/21/2007

Also see: Never keep your emotions bottled up

Also see: Silverlight 2 Beta 1 Cross Domain Bug

Also see: Tagspace, Meet Claimspace

Given easy way to compare two sequences, a C# 3.0 natural sort becomes roughly as trivial as the Python examples in Jeff’s blog:

string[] testItems = { “z24″, “z2″, “z15″, “z1″,
 “z3″, “z20″, “z5″, “z11″,
 “z 21″, “z22″ };
	
Func<string, object> convert = str =>
{ try { return int.Parse(str); }
 catch { return str; } };
var sorted = testItems.OrderBy(
 str => Regex.Split(str.Replace(” “, “”), “([0-9]+)”).Select(convert),
 new EnumerableComparer<object>());

It’s probably not meaningful to count lines of code. This being C#, I could have put it all on one line. As it is, I split it across more lines than I normally would, to avoid an annoying HTML layout issue. (I put my code samples in PRE blocks to get the formatting right, PRE blocks and long lines are a bad combination.) But I think it’s fair to say that any differences in size are due merely to syntactic differences between Python and C#. Structurally, there’s no substantial difference – I’ve been able to apply exactly the same techniques the Python examples used in C#.

Live Person: Live Chat Solution for Online Customer Service on Website.

Also see: DevWeek 2008 Silverlight Precon Demos

Also see: Life Calculus

Also see: The PDC and Application Compatibility, but still no Hosting

Also see: JSR-203 more New I/O APIs - NIO.2

Also see: Java design, operator overloading and people

Also see: Updated Finalization and Hosting

Also see: Resizing a Form has always been a pain in the rectum…

Also see: Snippet Compiler update

Also see: Interested in Artificial Intelligence? What about Wiki’s? Well, now you can have both.

Also see: Sometimes, it’s the small things..

If I print out the results using this code:

foreach (string s in sorted)
{
 Console.WriteLine(s);
}

It prints out the test items in this order:

z1
z2
z3
z5
z11
z15
z20
z 21
z22
z24

I.e., ascending numeric order, rather than what you’d get with most string ordering.

[Updated 21st December 2007: Charles Petzold didn’t like the original version, which treated spaces as significant for sorting. So I’ve updated the example to ignore spaces, as the position of “z 21” in the output above shows. I simply added a call to Replace(" ", "") on the string before passing it into Regex.Split.]


http://www.interact-sw.co.uk/iangblog/2007/12/13/natural-sorting

Comments »

The URI to TrackBack this entry is: http://cahtter.blogsome.com/2008/03/25/natural-sorting-in-c/trackback/

No comments yet.

RSS feed for comments on this post.

Leave a comment

Line and paragraph breaks automatic, e-mail address never displayed, HTML allowed: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <code> <em> <i> <strike> <strong>



Anti-spam measure: please retype the above text into the box provided.






















Get free blog up and running in minutes with Blogsome
Theme designed by Hadley Wickham