How can I use C# to sort values numerically?

.net, c#, numbers, sorting, string

Solution

Here's my working solution that also takes care of strings that are not in the right format (e.g. contain text).

The idea is to get the first number within both strings and compare these numbers. If they match, continue with the next number. If they don't, we have a winner. If one if these numbers isn't a number at all, do a string comparison of the part, which wasn't already compared.

It would be easy to make the comparer fully compatible to natural sort order by changing the way to determine the next number.

Look at that.. just found this question.

The Comparer:

class StringNumberComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int compareResult;
        int xIndex = 0, yIndex = 0;
        int xIndexLast = 0, yIndexLast = 0;
        int xNumber, yNumber;
        int xLength = x.Length;
        int yLength = y.Length;

        do
        {
            bool xHasNextNumber = TryGetNextNumber(x, ref xIndex, out xNumber);
            bool yHasNextNumber = TryGetNextNumber(y, ref yIndex, out yNumber);

            if (!(xHasNextNumber && yHasNextNumber))
            {
                // At least one the strings has either no more number or contains non-numeric chars
                // In this case do a string comparison of that last part
                return x.Substring(xIndexLast).CompareTo(y.Substring(yIndexLast));
            }

            xIndexLast = xIndex;
            yIndexLast = yIndex;

            compareResult = xNumber.CompareTo(yNumber);
        }
        while (compareResult == 0
            && xIndex < xLength
            && yIndex < yLength);

        return compareResult;
    }

    private bool TryGetNextNumber(string text, ref int startIndex, out int number)
    {
        number = 0;

        int pos = text.IndexOf('.', startIndex);
        if (pos < 0) pos = text.Length;

        if (!int.TryParse(text.Substring(startIndex, pos - startIndex), out number))
            return false;

        startIndex = pos + 1;

        return true;
    }
}

Usage:

public static void Main()
{
    var comparer = new StringNumberComparer();

    List<string> testStrings = new List<string>{
        "3.9.5.2.1.1",
        "3.9.5.2.1.10",
        "3.9.5.2.1.11",
        "3.9.test2",
        "3.9.test",
        "3.9.5.2.1.12",
        "3.9.5.2.1.2",
        "blabla",
        "....",
        "3.9.5.2.1.3",
        "3.9.5.2.1.4"};

    testStrings.Sort(comparer);

    DumpArray(testStrings);

    Console.Read();
}

private static void DumpArray(List<string> values)
{
    foreach (string value in values)
    {
        Console.WriteLine(value);
    }
}

Output:

....
3.9.5.2.1.1
3.9.5.2.1.2
3.9.5.2.1.3
3.9.5.2.1.4
3.9.5.2.1.10
3.9.5.2.1.11
3.9.5.2.1.12
3.9.test
3.9.test2
blabla

Problem

I have a string that contains numbers separated by periods. When I sort it appears like this since it is a string: (ascii char order) ``` 3.9.5.2.1.1 3.9.5.2.1.10 3.9.5.2.1.11 3.9.5.2.1.12 3.9.5.2.1.2 3.9.5.2.1.3 3.9.5.2.1.4 ``` etc. I want it to sort like this: (in numeric order) ``` 3.9.5.2.1.1 3.9.5.2.1.2 3.9.5.2.1.3 ... 3.9.5.2.1.9 3.9.5.2.1.10 3.9.5.2.1.11 3.9.5.2.1.12 ``` I know that I can: - Use the Split function to get the individual numbers - Put the values into an object - Sort the object I prefer to avoid all of that work if it is duplicating existing functionality. Is a method in the .net framework that does this already?

Original source

Related problems