Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can I use C# to sort values numerically?

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:

  1. Use the Split function to get the individual numbers
  2. Put the values into an object
  3. 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?

like image 385
Tarzan Avatar asked Dec 08 '10 16:12

Tarzan


People also ask

What can C be used for?

C is a powerful general-purpose programming language. It can be used to develop software like operating systems, databases, compilers, and so on. C programming is an excellent language to learn to program for beginners. Our C tutorials will guide you to learn C programming one step at a time.

How do I use C on my computer?

It is a bit more cryptic in its style than some other languages, but you get beyond that fairly quickly. C is what is called a compiled language. This means that once you write your C program, you must run it through a C compiler to turn your program into an executable that the computer can run (execute).

How is C used in everyday life?

C programming language is majorly used in the creation of hardware devices, operating systems, drivers, kernels, etc. It is also used for the development of GUIs and IDEs. For example: Linux Kernel is written in the C language.

What does %c mean in C?

%d is used to print decimal(integer) number ,while %c is used to print character . If you try to print a character with %d format the computer will print the ASCII code of the character.


3 Answers

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
like image 197
VVS Avatar answered Oct 06 '22 00:10

VVS


Is it possible for you to pad your fields to the same length on the front with 0? If so, then you can just use straight lexicographic sorting on the strings. Otherwise, there is no such method built in to the framework that does this automatically. You'll have to implement your own IComparer<string> if padding is not an option.

like image 40
jason Avatar answered Oct 06 '22 00:10

jason


Not really, though you may be able to use Regexes or Linq to avoid too much wheel-reinventing. Keep in mind it will cost you much the same computationally to use something built-in as to roll your own.

Try this:

List<string> myList = GetNumberStrings();

myList.Select(s=>s.Split('.')).ToArray().
   .Sort((a,b)=>RecursiveCompare(a,b))
   .Select(a=>a.Aggregate(new StringBuilder(),
      (s,sb)=>sb.Append(s).Append(".")).Remove(sb.Length-1, 1).ToString())
   .ToList();

...

public int RecursiveCompare(string[] a, string[] b)
{
    return RecursiveCompare(a,b,0)
}

public int RecursiveCompare(string[] a, string[] b, int index)
{
    return index == a.Length || index == b.Length 
        ? 0 
        : a[index] < b[index] 
            ? -1 
            : a[index] > b[index] 
                ? 1 
                : RecursiveCompare(a,b, index++);
}

Not the most compact, but it should work and you could use a y-combinator to make the comparison a lambda.

like image 35
KeithS Avatar answered Oct 05 '22 23:10

KeithS