126

I'm trying to sort an array of numbers that are strings and I'd like them to sort numerically.

The catch is that I cannot convert the numbers into int.

Here is the code:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => x))
{
    Console.WriteLine(thing);
}

Output:

101, 102, 103, 105, 90

I'd like:

90, 101, 102, 103, 105

EDIT: The output can't be 090, 101, 102...

Updated the code sample to say "things" instead of "sizes". The array can be something like this:

string[] things= new string[] { "paul", "bob", "lauren", "007", "90" };

That means it needs to be sorted alphabetically and by number:

007, 90, bob, lauren, paul
Amal K
  • 4,359
  • 2
  • 22
  • 44
sf.
  • 24,512
  • 13
  • 53
  • 58
  • 9
    Why can't you convert them to int? – Femaref Jun 18 '11 at 13:36
  • 2
    "sizes" can be something else like "name". The code sample is just simplified. – sf. Jun 18 '11 at 14:52
  • 4
    Will any of the numbers be negative? Will they all be integers? What's the range of the integers? – Eric Lippert Jun 18 '11 at 15:53
  • 1
    "things" can be any kind of string. I'd like the list to be sorted logically to a non computer literate person. Negative numbers should be before postive. In terms of string length, it wont be more than 100 chars. – sf. Jun 18 '11 at 17:21
  • 7
    How far do you want to go? Should `image10` come after `image2`? Should `January` come before `February`? – svick Jun 18 '11 at 18:32
  • 1
    For text lines that only start with a number: `var numList = tb_data.Lines.Select(x => new Tuple(Convert.ToInt16(x.Split(new char[] { ' ', '\t', ';' })[0]), x)).OrderBy(x => x.Item1).Select(x => x.Item2).ToArray(); tb_data.Lines = numList;` – TaW Aug 05 '20 at 09:31
  • The best answer I have found is from https://stackoverflow.com/a/5402865/18007679 – Jose Garcia Lopez Sep 05 '22 at 21:59
  • I run into the same issue when sorting codes like `followup_1_month`, `followup_12_month`, `followup_3_month`, `followup_6_month` or for example `gfi1`, `gfi10`, `gfi11`, `gfi2`, `gfi3`. iirc Modern CSV has a clever sort algorithm that accomodates exactly this. – BdR May 30 '23 at 09:46

24 Answers24

125

Pass a custom comparer into OrderBy. Enumerable.OrderBy will let you specify any comparer you like.

This is one way to do that:

void Main()
{
    string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "101"};

    foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer()))
    {    
        Console.WriteLine(thing);
    }
}


public class SemiNumericComparer: IComparer<string>
{
    /// <summary>
    /// Method to determine if a string is a number
    /// </summary>
    /// <param name="value">String to test</param>
    /// <returns>True if numeric</returns>
    public static bool IsNumeric(string value)
    {
        return int.TryParse(value, out _);
    }

    /// <inheritdoc />
    public int Compare(string s1, string s2)
    {
        const int S1GreaterThanS2 = 1;
        const int S2GreaterThanS1 = -1;

        var IsNumeric1 = IsNumeric(s1);
        var IsNumeric2 = IsNumeric(s2);

        if (IsNumeric1 && IsNumeric2)
        {
            var i1 = Convert.ToInt32(s1);
            var i2 = Convert.ToInt32(s2);

            if (i1 > i2)
            {
                return S1GreaterThanS2;
            }

            if (i1 < i2)
            {
                return S2GreaterThanS1;
            }

            return 0;
        }

        if (IsNumeric1)
        {
            return S2GreaterThanS1;
        }

        if (IsNumeric2)
        {
            return S1GreaterThanS2;
        }

        return string.Compare(s1, s2, true, CultureInfo.InvariantCulture);
    }
}
Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66
Jeff Paulsen
  • 2,132
  • 1
  • 12
  • 10
  • 1
    For the input given, this produces the same result as Recursive's answer, which involves PadLeft(). I'm assuming your input is actually more complex than this example shows, in which case a custom comparer is the way to go. – Jeff Paulsen Jun 18 '11 at 16:22
  • Cheers. This solution works and seems like an easy to read and clean way to implement. +1 for showing me you can use IComparer on OrderBy :) – sf. Jun 18 '11 at 20:31
  • 19
    The `IsNumeric` method is bad, an Exception driven coding is always bad. Use `int.TryParse` instead. Try your code with a large list and it will take forever. – Nean Der Thal Oct 25 '15 at 08:26
  • If it is helpful, I added an extension to this version [here](https://gist.github.com/bungard/b0ff50ddee4534de5443baff10ecfbe8) which adds support for sorting with words. For my needs, splitting on spaces was sufficient, and I had little need to worry about mixed use words (e.g. test12 vs test3), – matt.bungard Sep 14 '18 at 19:38
  • @NeanDerThal I'm pretty sure it's only slow/bad handling lots of exceptions in a loop, if you're debugging or you're accessing the Exception object. – Kelly Elton Apr 23 '19 at 21:43
  • Depending on the size of the list it may be a bad idea to parse repeatedly. – TaW Aug 05 '20 at 09:33
  • If both values are numeric we can return `i1.CompareTo(i2)` – Dariusz Nov 02 '21 at 08:49
119

Just pad with zeroes to the same length:

int maxlen = sizes.Max(x => x.Length);
var result = sizes.OrderBy(x => x.PadLeft(maxlen, '0'));
recursive
  • 83,943
  • 34
  • 151
  • 241
  • Nice idea but the next catch is that I need to diplay these values so the "90" needs to be a "90", not "090" – sf. Jun 18 '11 at 14:50
  • 12
    @sf: Try it, you might like the result. Remember, the order key is not the thing being ordered. If I said to order a list of customers by last name, then I get a list of customers, not a list of last names. If you say to order a list of strings by a transformed string then the result is the ordered list of original strings, not transformed strings. – Eric Lippert Jun 18 '11 at 15:49
  • I had to add "sizes = sizes.OrderBy(...)" to make this work. Is that normal or should the answer be edited? – gorgabal Apr 21 '20 at 15:10
  • 1
    @gorgabal: In general re-assigning to `sizes` wouldn't work either, because the result is a different type. The answer is kind of in short-hand, in that the second line shows the result as an expression, but it's up to the reader to do something with it. I've added another variable assignment to make that more clear. – recursive Apr 21 '20 at 17:35
  • @recursive thanks! I got confused because I recently used the `.sort()` function, which does not require reassignment. – gorgabal Apr 21 '20 at 20:46
  • I really wish I new why this works, but it is too late in the evening to care. – Joshua Lumley Oct 25 '20 at 08:51
  • Should be the selected answer – Desolator Nov 02 '21 at 01:22
105

Value is a string

List = List.OrderBy(c => c.Value.Length).ThenBy(c => c.Value).ToList();

Works

EchO
  • 1,274
  • 1
  • 10
  • 17
77

And, how about this ...

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

var size = from x in sizes
           orderby x.Length, x
           select x;

foreach (var p in size)
{
    Console.WriteLine(p);
}
Christian Specht
  • 35,843
  • 15
  • 128
  • 182
shenhengbin
  • 4,236
  • 1
  • 24
  • 33
24

There is a native function in windows StrCmpLogicalW that will compare in strings numbers as numbers instead of letters. It is easy to make a comparer that calls out to that function and uses it for it's comparisons.

public class StrCmpLogicalComparer : Comparer<string>
{
    [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
    private static extern int StrCmpLogicalW(string x, string y);

    public override int Compare(string x, string y)
    {
        return StrCmpLogicalW(x, y);
    }
}

It even works on strings that have both text and numbers. Here is a example program that will show the diffrence between the default sort and the StrCmpLogicalW sort

class Program
{
    static void Main()
    {
        List<string> items = new List<string>()
        {
            "Example1.txt", "Example2.txt", "Example3.txt", "Example4.txt", "Example5.txt", "Example6.txt", "Example7.txt", "Example8.txt", "Example9.txt", "Example10.txt",
            "Example11.txt", "Example12.txt", "Example13.txt", "Example14.txt", "Example15.txt", "Example16.txt", "Example17.txt", "Example18.txt", "Example19.txt", "Example20.txt"
        };

        items.Sort();

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }

        Console.WriteLine();

        items.Sort(new StrCmpLogicalComparer());

        foreach (var item in items)
        {
            Console.WriteLine(item);
        }
        Console.ReadLine();
    }
}

which outputs

Example1.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example2.txt
Example20.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt

Example1.txt
Example2.txt
Example3.txt
Example4.txt
Example5.txt
Example6.txt
Example7.txt
Example8.txt
Example9.txt
Example10.txt
Example11.txt
Example12.txt
Example13.txt
Example14.txt
Example15.txt
Example16.txt
Example17.txt
Example18.txt
Example19.txt
Example20.txt
Scott Chamberlain
  • 124,994
  • 33
  • 282
  • 431
6

This site discusses alphanumeric sorting and will sort the numbers in a logical sense instead of an ASCII sense. It also takes into account the alphas around it:

http://www.dotnetperls.com/alphanumeric-sorting

EXAMPLE:

  • C:/TestB/333.jpg
  • 11
  • C:/TestB/33.jpg
  • 1
  • C:/TestA/111.jpg
  • 111F
  • C:/TestA/11.jpg
  • 2
  • C:/TestA/1.jpg
  • 111D
  • 22
  • 111Z
  • C:/TestB/03.jpg

  • 1
  • 2
  • 11
  • 22
  • 111D
  • 111F
  • 111Z
  • C:/TestA/1.jpg
  • C:/TestA/11.jpg
  • C:/TestA/111.jpg
  • C:/TestB/03.jpg
  • C:/TestB/33.jpg
  • C:/TestB/333.jpg

The code is as follows:

class Program
{
    static void Main(string[] args)
    {
        var arr = new string[]
        {
           "C:/TestB/333.jpg",
           "11",
           "C:/TestB/33.jpg",
           "1",
           "C:/TestA/111.jpg",
           "111F",
           "C:/TestA/11.jpg",
           "2",
           "C:/TestA/1.jpg",
           "111D",
           "22",
           "111Z",
           "C:/TestB/03.jpg"
        };
        Array.Sort(arr, new AlphaNumericComparer());
        foreach(var e in arr) {
            Console.WriteLine(e);
        }
    }
}

public class AlphaNumericComparer : IComparer
{
    public int Compare(object x, object y)
    {
        string s1 = x as string;
        if (s1 == null)
        {
            return 0;
        }
        string s2 = y as string;
        if (s2 == null)
        {
            return 0;
        }

        int len1 = s1.Length;
        int len2 = s2.Length;
        int marker1 = 0;
        int marker2 = 0;

        // Walk through two the strings with two markers.
        while (marker1 < len1 && marker2 < len2)
        {
            char ch1 = s1[marker1];
            char ch2 = s2[marker2];

            // Some buffers we can build up characters in for each chunk.
            char[] space1 = new char[len1];
            int loc1 = 0;
            char[] space2 = new char[len2];
            int loc2 = 0;

            // Walk through all following characters that are digits or
            // characters in BOTH strings starting at the appropriate marker.
            // Collect char arrays.
            do
            {
                space1[loc1++] = ch1;
                marker1++;

                if (marker1 < len1)
                {
                    ch1 = s1[marker1];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch1) == char.IsDigit(space1[0]));

            do
            {
                space2[loc2++] = ch2;
                marker2++;

                if (marker2 < len2)
                {
                    ch2 = s2[marker2];
                }
                else
                {
                    break;
                }
            } while (char.IsDigit(ch2) == char.IsDigit(space2[0]));

            // If we have collected numbers, compare them numerically.
            // Otherwise, if we have strings, compare them alphabetically.
            string str1 = new string(space1);
            string str2 = new string(space2);

            int result;

            if (char.IsDigit(space1[0]) && char.IsDigit(space2[0]))
            {
                int thisNumericChunk = int.Parse(str1);
                int thatNumericChunk = int.Parse(str2);
                result = thisNumericChunk.CompareTo(thatNumericChunk);
            }
            else
            {
                result = str1.CompareTo(str2);
            }

            if (result != 0)
            {
                return result;
            }
        }
        return len1 - len2;
    }
}
2Yootz
  • 3,971
  • 1
  • 36
  • 31
6

try this

sizes.OrderBy(x => Convert.ToInt32(x)).ToList<string>();

Note: this will helpful when all are string convertable to int.....

Pranay Rana
  • 175,020
  • 35
  • 237
  • 263
5

You say you cannot convert the numbers into int because the array can contain elements that cannot be converted to int, but there is no harm in trying:

string[] things = new string[] { "105", "101", "102", "103", "90", "paul", "bob", "lauren", "007", "90" };
Array.Sort(things, CompareThings);

foreach (var thing in things)
    Debug.WriteLine(thing);

Then compare like this:

private static int CompareThings(string x, string y)
{
    int intX, intY;
    if (int.TryParse(x, out intX) && int.TryParse(y, out intY))
        return intX.CompareTo(intY);

    return x.CompareTo(y);
}

Output: 007, 90, 90, 101, 102, 103, 105, bob, lauren, paul

Ulf Kristiansen
  • 1,571
  • 3
  • 22
  • 34
5

I guess this will be much more good if it has some numeric in the string. Hope it will help.

PS:I'm not sure about performance or complicated string values but it worked good something like this:

lorem ipsum
lorem ipsum 1
lorem ipsum 2
lorem ipsum 3
...
lorem ipsum 20
lorem ipsum 21

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        int s1r, s2r;
        var s1n = IsNumeric(s1, out s1r);
        var s2n = IsNumeric(s2, out s2r);

        if (s1n && s2n) return s1r - s2r;
        else if (s1n) return -1;
        else if (s2n) return 1;

        var num1 = Regex.Match(s1, @"\d+$");
        var num2 = Regex.Match(s2, @"\d+$");

        var onlyString1 = s1.Remove(num1.Index, num1.Length);
        var onlyString2 = s2.Remove(num2.Index, num2.Length);

        if (onlyString1 == onlyString2)
        {
            if (num1.Success && num2.Success) return Convert.ToInt32(num1.Value) - Convert.ToInt32(num2.Value);
            else if (num1.Success) return 1;
            else if (num2.Success) return -1;
        }

        return string.Compare(s1, s2, true);
    }

    public bool IsNumeric(string value, out int result)
    {
        return int.TryParse(value, out result);
    }
}
3

The answer given by Jeff Paulsen is correct but the Comprarer can be much simplified to this:

public class SemiNumericComparer: IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (IsNumeric(s1) && IsNumeric(s2))
          return Convert.ToInt32(s1) - Convert.ToInt32(s2)

        if (IsNumeric(s1) && !IsNumeric(s2))
            return -1;

        if (!IsNumeric(s1) && IsNumeric(s2))
            return 1;

        return string.Compare(s1, s2, true);
    }

    public static bool IsNumeric(object value)
    {
        int result;
        return Int32.TryParse(value, out result);
    }
}

This works because the only thing that is checked for the result of the Comparer is if the result is larger, smaller or equal to zero. One can simply subtract the values from another and does not have to handle the return values.

Also the IsNumeric method should not have to use a try-block and can benefit from TryParse.

And for those who are not sure: This Comparer will sort values so, that non numeric values are always appended to the end of the list. If one wants them at the beginning the second and third if block have to be swapped.

k-nut
  • 3,447
  • 2
  • 18
  • 28
  • As calling the TryParse method probably has some overhead, I would store the the isNumeric values for s1 and s2 into boolean values first and do the comparison on those instead. This way they are not evaluated multiple times. – Optavius Dec 20 '16 at 15:11
3

This seems a weird request and deserves a weird solution:

string[] sizes = new string[] { "105", "101", "102", "103", "90" };

foreach (var size in sizes.OrderBy(x => {
    double sum = 0;
    int position = 0;
    foreach (char c in x.ToCharArray().Reverse()) {
        sum += (c - 48) * (int)(Math.Pow(10,position));
        position++;
    }
    return sum;
}))

{
    Console.WriteLine(size);
}
Bohemian
  • 412,405
  • 93
  • 575
  • 722
Marino Šimić
  • 7,318
  • 1
  • 31
  • 61
  • I meant 0x30 of course. Also, the array still could contain a non-numeric string, for which the solution will produce interesting results. – Femaref Jun 18 '11 at 13:56
  • And note that the -48 or not changes absolutely nothing, we could directly use the integer value of the char, so remove that -48 if it bothers you... – Marino Šimić Jun 18 '11 at 13:59
  • The char value is 0x30, if you convert that to int, it will still be 0x30, which isn't the number 0. – Femaref Jun 18 '11 at 14:01
  • The only thing converted to integer is the double that is returned from Math.Pow – Marino Šimić Jun 18 '11 at 14:01
  • femaref it doesnt matter if it is zero or not, the decadic system takes care of that, it could be an Đ if you want to, the only thing that matters that the numbers are in ascending orders in the character set, and that are less than 10 – Marino Šimić Jun 18 '11 at 14:02
  • @Marino: Doesn't `sum` represent the entire value represented as an int? – recursive Jun 18 '11 at 14:05
  • yes it does, we can argue what conversion is meant here, this sum of the character value as the decadic system works is actually a conversion :D – Marino Šimić Jun 18 '11 at 14:09
  • btw i like your that pads the string with zeroes, i'll give you a +1 if you adapt it to work on strings of any lenght :)) – Marino Šimić Jun 18 '11 at 14:11
  • i now declared sum as double, is this enough to avoid the obstacle? :P – Marino Šimić Jun 18 '11 at 14:13
2
public class NaturalSort: IComparer<string>
{
          [DllImport("shlwapi.dll", CharSet = CharSet.Unicode)]
          public static extern int StrCmpLogicalW(string x, string y);

          public int Compare(string x, string y)
          {
                 return StrCmpLogicalW(x, y);
          }
}

arr = arr.OrderBy(x => x, new NaturalSort()).ToArray();

The reason I needed it was to get filed in a directory whose filenames started with a number:

public static FileInfo[] GetFiles(string path)
{
  return new DirectoryInfo(path).GetFiles()
                                .OrderBy(x => x.Name, new NaturalSort())
                                .ToArray();
}
Neil M
  • 171
  • 8
1

Try this :

string[] things= new string[] { "105", "101", "102", "103", "90" };

int tmpNumber;

foreach (var thing in (things.Where(xx => int.TryParse(xx, out tmpNumber)).OrderBy(xx =>     int.Parse(xx))).Concat(things.Where(xx => !int.TryParse(xx, out tmpNumber)).OrderBy(xx => xx)))
{
    Console.WriteLine(thing);
}
TMAmine
  • 611
  • 3
  • 4
1

Expanding on Jeff Paulsen answer. I wanted to make sure it didn't matter how many number or char groups were in the strings:

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (int.TryParse(s1, out var i1) && int.TryParse(s2, out var i2))
        {
            if (i1 > i2)
            {
                return 1;
            }

            if (i1 < i2)
            {
                return -1;
            }

            if (i1 == i2)
            {
                return 0;
            }
        }

        var text1 = SplitCharsAndNums(s1);
        var text2 = SplitCharsAndNums(s2);

        if (text1.Length > 1 && text2.Length > 1)
        {

            for (var i = 0; i < Math.Max(text1.Length, text2.Length); i++)
            {

                if (text1[i] != null && text2[i] != null)
                {
                    var pos = Compare(text1[i], text2[i]);
                    if (pos != 0)
                    {
                        return pos;
                    }
                }
                else
                {
                    //text1[i] is null there for the string is shorter and comes before a longer string.
                    if (text1[i] == null)
                    {
                        return -1;
                    }
                    if (text2[i] == null)
                    {
                        return 1;
                    }
                }
            }
        }

        return string.Compare(s1, s2, true);
    }

    private string[] SplitCharsAndNums(string text)
    {
        var sb = new StringBuilder();
        for (var i = 0; i < text.Length - 1; i++)
        {
            if ((!char.IsDigit(text[i]) && char.IsDigit(text[i + 1])) ||
                (char.IsDigit(text[i]) && !char.IsDigit(text[i + 1])))
            {
                sb.Append(text[i]);
                sb.Append(" ");
            }
            else
            {
                sb.Append(text[i]);
            }
        }

        sb.Append(text[text.Length - 1]);

        return sb.ToString().Split(' ');
    }
}

I also took SplitCharsAndNums from an SO Page after amending it to deal with file names.

Tod
  • 2,070
  • 21
  • 27
1

Example of short IComparer class.

  1. if both string arguments can be converted to integer then arguments are parsed to integers and compared
  2. if only one argument can be converted to integer, then integer is prioritized (has lower value) and are inserted before string.
  3. If no one of arguments can be converted into integer then ordinary string comparison is used.

Code:

public class CompareIntegerStrings : IComparer<string>
    {
        public int Compare(string x, string y)
        {
            if (int.TryParse(x, out int xOut) && int.TryParse(y, out int yOut))
                return xOut.CompareTo(yOut);
            else if (int.TryParse(x, out _))
                return -1;
            else if (int.TryParse(y, out _))
                return 1;
            else
                return x.CompareTo(y);
    
        }
    }

In this example

List<string> intStrings = new List<string> { "01","0022","abba", "11", "deep purple", "02", };
List<string> orderedIntStrings = intStrings.OrderBy(i=>i,new CompareIntegerStrings()).ToList();

ordered list orderedIntString are { "01","02","11","0022","abba","deep purple"}.

1

Using Regex.Replace is so simple yet efficient. Note that the number "3" just has to be a number equal-to or larger than your longest string, so for anyone else, increase as needed.

using System.Text.RegularExpressions;

string[] things = new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => Regex.Replace(x, @"\d+", i => 
i.Value.PadLeft(3, '0'))))
{
    Console.WriteLine(thing);
}
Crazy Cat
  • 1,332
  • 15
  • 19
1

Recommend using NaturalSort.Extension(nuget/github), as it is a reasonably difficult operation as you can see from the answer.

using NaturalSort.Extension;
var ordered = things.OrderBy(x => x, StringComparison.OrdinalIgnoreCase.WithNaturalSort());
mikm
  • 111
  • 1
  • 4
1

Another way to solve this, that also allows combinations of text and numbers, is to use to find all numbers via regex and pad them with 0's.

To do that we can create PadNumbers function:

public static string PadNumbers(string input)
{
    return Regex.Replace(input, "[0-9]+", match => match.Value.PadLeft(10, '0'));
}

I And then use it like this:

string[] things= new string[] { "lara", "bob", "lara11", "lara2", "lara007" };
var sortedThings = things.OrderBy(x => PadNumbers(x)).ToList();

The padding will only happen for the comparison. The result will still contain the original strings (without padding).

The result order would be:

"bob"
"lara"
"lara2"
"lara007"
"lara11"
Wolfware
  • 51
  • 4
0

My preferred solution (if all strings are numeric only):

// Order by numerical order: (Assertion: all things are numeric strings only) 
foreach (var thing in things.OrderBy(int.Parse))
{
    Console.Writeline(thing);
}
Bug Raptor
  • 261
  • 6
  • 15
0
public class Test
{
    public void TestMethod()
    {
        List<string> buyersList = new List<string>() { "5", "10", "1", "str", "3", "string" };
        List<string> soretedBuyersList = null;

        soretedBuyersList = new List<string>(SortedList(buyersList));
    }

    public List<string> SortedList(List<string> unsoredList)
    {
        return unsoredList.OrderBy(o => o, new SortNumericComparer()).ToList();
    }
}

   public class SortNumericComparer : IComparer<string>
{
    public int Compare(string x, string y)
    {
        int xInt = 0;
        int yInt = 0;
        int result = -1;

        if (!int.TryParse(x, out xInt))
        {
            result = 1;
        }

        if(int.TryParse(y, out yInt))
        {
            if(result == -1)
            {
                result = xInt - yInt;
            }
        }
        else if(result == 1)
        {
             result = string.Compare(x, y, true);
        }

        return result;
    }
}
kumar
  • 1
  • 2
0
Try this out..  



  string[] things = new string[] { "paul", "bob", "lauren", "007", "90", "-10" };

        List<int> num = new List<int>();
        List<string> str = new List<string>();
        for (int i = 0; i < things.Count(); i++)
        {

            int result;
            if (int.TryParse(things[i], out result))
            {
                num.Add(result);
            }
            else
            {
                str.Add(things[i]);
            }


        }

Now Sort the lists and merge them back...

        var strsort = from s in str
                      orderby s.Length
                      select s;

        var numsort = from n in num
                     orderby n
                     select n;

        for (int i = 0; i < things.Count(); i++)
        {

         if(i < numsort.Count())
             things[i] = numsort.ElementAt(i).ToString();
             else
             things[i] = strsort.ElementAt(i - numsort.Count());               
               }

I jsut tried to make a contribution in this interesting question...

Syeda
  • 1,215
  • 11
  • 23
0

I would have commented under recursive's answer, but my reputation is too low for that.

Because recursive's answer only works with numeric strings (if You have a string like "I am just a damn long string", it would be sorted after "Not so long string") and OP edited his answer, my Idea for the question would be to sort the strings by differentiating them into numbers and not numbers:

int maxlen = items.Max(x => x.Length);
var items  = items.OrderBy(x => long.TryParse(x, out _) == true ? x.PadLeft(maxlen, '0') : x);

The underscore is for discarding the output

Lee Taylor
  • 7,761
  • 16
  • 33
  • 49
Gubbel
  • 1
  • 1
-1
namespace X
{
    public class Utils
    {
        public class StrCmpLogicalComparer : IComparer<Projects.Sample>
        {
            [DllImport("Shlwapi.dll", CharSet = CharSet.Unicode)]
            private static extern int StrCmpLogicalW(string x, string y);


            public int Compare(Projects.Sample x, Projects.Sample y)
            {
                string[] ls1 = x.sample_name.Split("_");
                string[] ls2 = y.sample_name.Split("_");
                string s1 = ls1[0];
                string s2 = ls2[0];
                return StrCmpLogicalW(s1, s2);
            }
        }

    }
}
Muhammad Touseef
  • 4,357
  • 4
  • 31
  • 75
-2

Even though this is an old question, I'd like to give a solution:

string[] things= new string[] { "105", "101", "102", "103", "90" };

foreach (var thing in things.OrderBy(x => Int32.Parse(x) )
{
    Console.WriteLine(thing);
}

Woha quite simple right? :D

Oscar Ortiz
  • 813
  • 8
  • 16