1

I have some strings in a list

List<string> list = new List<string>{ "100-1", "100-11", "100-3", "100-20" }

I used following code to sort which is picked from this location

void Main()
{
    string[] things= new string[] { "100-1", "100-11", "100-3", "100-20" };

    foreach (var thing in things.OrderBy(x => x, new SemiNumericComparer()))
    {    
        Console.WriteLine(thing);
    }
}
    
public class SemiNumericComparer: IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        if (IsNumeric(s1) && IsNumeric(s2))
        {
            if (Convert.ToInt32(s1) > Convert.ToInt32(s2)) return 1;
            if (Convert.ToInt32(s1) < Convert.ToInt32(s2)) return -1;
            if (Convert.ToInt32(s1) == Convert.ToInt32(s2)) return 0;
        }

        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)
    {
        try {
            int i = Convert.ToInt32(value.ToString());
            return true; 
        }
        catch (FormatException) {
            return false;
        }
    }
}

My output is 100-1, 100-11, 100-20, 100-3

I believe it is taking - as decimal and comparing the values. Actually I was expecting the result to be 100-1, 100-3, 100-11, 100-20. I just wanted to know on what basis it is actually performing sort. Any help is appreciated. Even I expect it to treat 100-2 and 100-20 differently.

Just on the fly, I have seen in Infragistic control grid that sorting in it produces the same result as I was expecting here to be.

I have many other string values in the list, some are integers, doubles and so on. Hyphen is just a case mentioned here.

gunr2171
  • 16,104
  • 25
  • 61
  • 88
Sandy
  • 11,332
  • 27
  • 76
  • 122
  • 1
    If some elements in your list _were_ numbers (without dashes) you would call `Convert.ToInt32` twice for each of them. And it's not the best option to use `try`-`catch`. Check out the `int.TryParse` method instead. It will check if it is a number or not, _and_ give you the number, in one operation. And you won't need to catch exceptions. And you can specify some "number styles" that `s1` and `s2` must obey in order to be "good" integers. – Jeppe Stig Nielsen Dec 03 '12 at 09:33
  • It's sorting them as strings. Imagine if the numbers where letters: 1 => a; 3=> c; and 11 => aa. Then it would sort as a, aa, c not a, c, aa. You'll have to treat strings with hyphens or any other delimiters as special cases in your comparer to get the desired result. – juharr Dec 03 '12 at 10:20

3 Answers3

4
var sorted = things.Select(s => s.Split('-'))
                .OrderBy(x => double.Parse(x[0]))
                .ThenBy(x => double.Parse(x[1]))
                .Select(x=>String.Join("-",x))
                .ToList();
L.B
  • 114,136
  • 19
  • 178
  • 224
  • will it be feasible to it as user can have different special character rather than `-`?? Its a just a doubt :) – Sandy Dec 03 '12 at 09:25
  • @rapsalands You can split your string using any char `s.Split(new char[]{'-','#'})` – L.B Dec 03 '12 at 09:26
2

This should work as expected:

string[] things= new string[] { "100-1", "100-11", "100-3", "100-20" };
IEnumerable<string> ordered = things
            .Select(s => new
            {
                str = s,
                firstPart = s.Split('-').ElementAtOrDefault(0),
                secondPart = s.Split('-').ElementAtOrDefault(1)
            })
            .OrderBy(x => int.Parse(x.firstPart))
            .ThenBy(x => int.Parse(x.firstPart))
            .Select(x => x.str);

foreach (string s in ordered)
    Console.WriteLine(s);

Although it assumes that your data is strict, otherwise you're open for exceptions, f.e at int.Parse(x.firstPart).

Demo: http://ideone.com/UJ5Yt4

Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • will it be feasible to it as user can have different special character rather than `-`?? Its a just a doubt :) – Sandy Dec 03 '12 at 09:24
  • @rapsalands: You could use [**this overload**](http://msdn.microsoft.com/en-us/library/ms131448.aspx) to use multiple separators. But if your input is arbitrary you cannot use split. But then something is going wrong. – Tim Schmelter Dec 03 '12 at 09:31
1

If you want to sort the items by the 2nd number (after hyphen), You need to parse the string to a number then order by using it. you can try:

string[] things = new string[] { "100-1", "100-11", "100-3", "100-20" };
var test = things.OrderBy(r => int.Parse(r.Split('-')[1])).ToArray();

The reason your current code is not working is probably due to the fact that it can't parse the string 100- to an integer value and your function IsNumeric is returning false.

Habib
  • 219,104
  • 29
  • 407
  • 436
  • +1...yep...IsNumeric is returning false. But even if it is taking the values as strings, then on what basis it is giving me this output?? also see edited post where I have mentioned that I have many other kind of strings (int, double as strings) along with this kind. Will it be feasible to do this bcoz user may also enter special characters apart from hyphen?? – Sandy Dec 03 '12 at 09:22
  • 1
    @rapsalands Your code is always falling through to `string.Compare(s1, s2, true);` because `123-45` _isn't a number_. You're getting a normal string ordeing. – Rawling Dec 03 '12 at 09:24