-1

I have a list of strings something like:-

"008","a", "007","b", "c","009"

Need OutPut:-

"a", "b", "c", "007", "008", "009" What i tried so far

string[] things= new string[] {  "aaul", "bob", "lauren", "007", "008", "009"};



foreach (var thing in things.AsQueryable().OrderByDescending(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 0;
            if (Convert.ToInt32(s1) < Convert.ToInt32(s2)) return 1;
            if (Convert.ToInt32(s1) == Convert.ToInt32(s2)) return -1;
        }

        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;
        }
    }
}

I Already tried

Here is problem fiddle using above solution

I am able to get output like

a,b,c,009,008,007 Or 007,008,009,c,b,a but need a,b,c,007,008,009 Any Idea?

jitender
  • 10,238
  • 1
  • 18
  • 44
  • Is `"1234"` possible? – Tim Schmelter Jun 09 '17 at 11:45
  • I didn't downvote you. Just found your thread, but what is the question?(Your code is not working??) – Bigeyes Jun 09 '17 at 11:45
  • i am able to get output like a,b,c,009,008,007 but need a,b,c,007,008,009 – jitender Jun 09 '17 at 11:48
  • Possible duplicate of [c# linq orderby numbers that are string (and you cannot convert them to int)](https://stackoverflow.com/questions/6396378/c-sharp-linq-orderby-numbers-that-are-string-and-you-cannot-convert-them-to-int) – Chris Jun 09 '17 at 12:05
  • 1
    @fubo: Nor is that question if you read the edits. And indeed the accepted answer is so similar to this code that I have a strong suspicion that the OP took that code and then broke it... – Chris Jun 09 '17 at 13:00
  • @Chris I already tried that answer and doesn't meet up my requirements you can see in the fiddle i provide above – jitender Jun 09 '17 at 13:04
  • @jitender: Do you understand how that solution works? It feels like you've just copied and pasted it without any attempt to actually understand it. If you understood how that solution works you would realise that the only thing you needed to change was the ordering of the numeric and non-numeric elements which is pretty easy to do and if you just look at the conditionals using `IsNumeric` it should be very obvious where these changes need to be too. – Chris Jun 09 '17 at 13:11
  • Stack overflow is not a code generator. When there is a question virtually identical to yours with a perfectly decent answer then you should expect your question to be closed as a duplicate (though you may be lucky and have people do the work for you as well). – Chris Jun 09 '17 at 13:13
  • @Chris Sorry bro I understand what you are trying to say I don't post any question before trying my best here also I tried to modify that answer but didn't get actually how it was working.Also, that's how experience comes up.You can't expect that a 2-year experience guy can do everything that a 10-year experience guy can. – jitender Jun 09 '17 at 13:18
  • @jitender: I would expect anybody that understand how the language works to be able to read a method like that and read any related documenation to be able to work out what that is doing and what it needs. A 10 year experience programmer isn't necessarily better at understanding code, they just have experience of a lot more patterns, know a lot more gotchas, etc. If you have 2 years experience in c# then I would 100% expect you to be able to understand exactly how that answer works. It would probably take you longer but you should be able to. – Chris Jun 09 '17 at 13:55

7 Answers7

4

You want to order into two group, first those which aren't integers followed by the integeres. The first group is ordered lexicographically while the numbers are ordered by their value?

I'd use an extension method like following that try-parses the string to int:

public static int? TryGetInt(this string item)
{
    int i;
    bool success = int.TryParse(item, out i);
    return success ? (int?)i : (int?)null;
}

Then you could use this LINQ query to get the desired order:

string[] things = new string[] { "008", "bob", "009", "007", "aaul", "lauren" };

var ordered = things
    .Select(s => new { s, numberOrNull = s.TryGetInt() }) // store in anonymous type
    .OrderBy(x => x.numberOrNull.HasValue ? 1 : 0)        // first non-integer group
    .ThenBy(x => x.numberOrNull.HasValue ? x.numberOrNull.Value : int.MinValue) // then by number value
    .ThenBy(x => x.s)  // if they weren't numbers order lexicographically
    .Select(x => x.s); // select the string
Tim Schmelter
  • 450,073
  • 74
  • 686
  • 939
  • `.OrderBy(x => x.numberOrNull.HasValue ? x.numberOrNull.Value : int.MinValue)` saves one iteration – fubo Jun 09 '17 at 13:06
  • @fubo: true, clever trick. Has just one (theoretical) issue, if `int.MinValue` could be a valid integer value here then you wouldn't differentiate between the non-integer and integer group. So `int.MinValue` would come first even if it should belong to the integer group. Also note that the `ThenBy` calls are only made if the previous `OrderBy` yielded two equal values not always, so there is not a performance issue. – Tim Schmelter Jun 09 '17 at 13:13
2

First separate different groups of starting letter (digit or non-digit), then sort each group

things.OrderBy(x => char.IsDigit(x.FirstOrDefault())).ThenBy(x => x)
grek40
  • 13,113
  • 1
  • 24
  • 50
2
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;
        }
    }
}

Taken from here and modified it to sort letters first

Use OrderBy instead of OrderByDescending

Biesi
  • 800
  • 9
  • 17
2

Sort by int / not int first, then by content

string[] things = new string[] { "a", "b", "c", "009", "008", "007" };
int temp=0;
string[] result = things.OrderBy(x => int.TryParse(x, out temp))
                        .ThenBy(x => x)
                        .ToArray();

Update to Tim's comment (natural order for the int values and lexicographical for the strings)

string[] things = new string[] { "c", "b", "a", "009", "008", "007" , "1234", "299" };
int temp = 0;
string[] result = things.OrderBy(x => int.TryParse(x, out temp) ? temp : int.MinValue)
                        .ThenBy(x => x)
                        .ToArray();
fubo
  • 44,811
  • 17
  • 103
  • 137
  • Using a local variable as storage for the ints is dangerous in LINQ queries. If you change the query you might get undesired results. To quote [E.Lippert](https://blogs.msdn.microsoft.com/ericlippert/2012/08/14/out-parameters-and-linq-do-not-mix/): _"it is a “worst practice” to mutate a variable like this.... Try to always avoid side effects in queries."_ – Tim Schmelter Jun 09 '17 at 12:31
  • 1
    @TimSchmelter isn't that limited to cases where we actually use the variable? I don't see a problem with this write-only scenario (other than unnecessary boxing). – grek40 Jun 09 '17 at 12:34
  • @grek40: well, if OP actually wants to order by that variable ascending/descending(as his desired result suggests) he needs to use it. This approach always orders lexicographically which could be a problem with numbers like `1234` and `299`. That's why i've first asked if "1234" is possible. – Tim Schmelter Jun 09 '17 at 12:36
2

Try the following with Regex:

string[] str = { "008", "abhi", "007", "bcd", "cde", "009" };
str = str.OrderBy(x => Regex.IsMatch(x, @"^\d+$")).ThenBy(x => x).ToArray<string>();
Abhishek
  • 2,925
  • 4
  • 34
  • 59
2

What you have done wrong is the implementation of the Compare method.

Look carefully at the docs:

Return value

A signed integer that indicates the relative values of x and y, as shown in the following table.

Value                 Meaning
Less than zero        x is less than y.
Zero                  x equals y.
Greater than zero     x is greater than y.

You are returning the reverse of what it should in the "both strings are numeric" case. Just switch that around and it will work:

 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;
Community
  • 1
  • 1
Sweeper
  • 213,210
  • 22
  • 193
  • 313
  • You seem to identify the error but then not fix it properly... You should not be returning 1 when the two items are equal... – Chris Jun 09 '17 at 11:59
  • This is the major bug fixed and hopefully the OP can fix the rest of the issues themselves but it is still not doing quite the right thing... eg if order by descending as the OP is then the words and numbers come out in the wrong order. If you sort by ascending then the numbers come out before the words. Relatively easy fixes once the thing is being consistent but maybe worth mentioning? – Chris Jun 09 '17 at 12:02
  • @Chris I assumed that OP just wanted to order them ascendingly. Also, why is "numbers come before letters" inconsistent with "numbers come after letters"? IMO, when you order something and the numbers come first, it is the most natural thing that when you order it in reverse, the numbers come last. – Sweeper Jun 09 '17 at 12:07
1

Try this:

public class SemiNumericComparer : IComparer<string>
{
    public int Compare(string s1, string s2)
    {
        int i1, i2;
        bool b1 = int.TryParse(s1, out i1);
        bool b2 = int.TryParse(s2, out i2);
        if (b1 && b2)
        {
            return i1.CompareTo(i2);
        }
        if (b1) return 1;
        if (b2) return -1;
        return string.Compare(s1, s2, true);
    }
}
Pablo notPicasso
  • 3,031
  • 3
  • 17
  • 22