Numeric String Sort in C#

Scenario: you have a List<string> collection where the contents could be alpha, numeric, or alphanumeric. If you just sort the collection using the built-in Sort() method, it will use string.CompareTo(), treating each item in the collection as a string. Thus a collection with the following values ["1", "44", "22", 4", "5"] will end up being sorted as ["1", "22", "4", "44", "5"] – even though you might want the items to be treated as numbers (in which case they would be sorted as ["1", "4", "5", "22", "44"].

The solution to this is to use a custom comparer class. I put the following together:

public class NumericStringSort : IComparer {

    int IComparer.Compare(string a, string b) {
        decimal aDec;
        decimal bDec;
        if (decimal.TryParse(a, out aDec) && decimal.TryParse(b, out bDec)) {
            return aDec.CompareTo(bDec);
        } else {
            return a.CompareTo(b);
        }
    }

    public static IComparer NumericStringSorter() {
        return (IComparer) new NumericStringSort();
    }
}

As you can see, the class implements the IComparer interface. If both of the strings can be converted into decimals, then the decimal comparison is used. Otherwise the string comparison is used.

You can see a usage example through the following set of unit tests (as you can see, it also handles mixed alphanumeric sets gracefully, sorting all numeric items to the front of the list in numeric order, followed by all alpha entries in alphabetical order):

[TestFixture]
public class NumericStringSortTests {

    [Test]
    public void TestNumericSort_AllNumeric() {
        List items = new List { "1", "44", "22", "4", "5" };
        items.Sort(NumericStringSort.NumericStringSorter());
        List expectedItems = new List { "1", "4", "5", "22", "44" };
        Assert.IsTrue(items.SequenceEqual(expectedItems));
    }

    [Test]
    public void TestNumericSort_MixesAlphaNumeric() {
        List items = new List { "a", "c", "d", "b", "1", "2", "22", "3" };
        items.Sort(NumericStringSort.NumericStringSorter());
        List expectedItems = new List { "1", "2", "3", "22", "a", "b", "c", "d" };
        Assert.IsTrue(items.SequenceEqual(expectedItems));
    }

    [Test]
    public void TestNumericSort_AllAlpha() {
        List items = new List { "a", "c", "d", "b" };
        items.Sort(NumericStringSort.NumericStringSorter());
        List expectedItems = new List { "a", "b", "c", "d" };
        Assert.IsTrue(items.SequenceEqual(expectedItems));
    }
}

This is something that I have needed on previous occasions – perhaps it may be of use to someone else.