19

A sort of:

Documenti = Documenti
    .OrderBy(o => string.IsNullOrEmpty(o.Note))
    .ThenBy(o => Int32.TryParse(o.Note))
    .ToList();

That will "ignore" (not order, putting at the end) if o.Note is "" or not an int.

How can I do it?

2
  • Could you try rewording this question using full sentences? Maybe it will be more clear to others but I have no idea what you are asking. Commented May 17, 2013 at 15:52
  • are you using this with EntityFramework Commented May 17, 2013 at 15:52

5 Answers 5

32

Everyone who uses C#7 or newer scroll to the bottom, everyone else can read the original answer:


Yes, you can, if you pass the correct parameters to int.TryParse. Both overloads take the int as out-parameter and initialize it inside with the parsed value. So like this:

int note;
Documenti = Documenti
    .OrderBy(o => string.IsNullOrEmpty(o.Note))
    .ThenBy(o => Int32.TryParse(o.Note, out note)) 
    .ToList();

The clean approach is using a method that parses to int and returns int? if unparseable:

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

Now you can use this query(OrderByDescending because true is "greater" than false):

Documenti = Documenti.OrderByDescending(d => d.Note.TryGetInt().HasValue).ToList();

It's cleaner than using a local variable that is used in int.TryParse as out parameter.

Eric Lippert commented another answer of me where he gives an example when it might hurt:

C# LINQ: How is string("[1, 2, 3]") parsed as an array?


Update, this has changed with C#7. Now you can declare the variable directly where you use out parameters:

Documenti = Documenti
.OrderBy(o => string.IsNullOrEmpty(o.Note))
.ThenBy(o => Int32.TryParse(o.Note, out int note)) 
.ToList();
Sign up to request clarification or add additional context in comments.

2 Comments

Would the ThenBy be sorting the boolean success/fail returned from TryParse rather than double value in this case?
@BenjaminBrandt: ThenBy will use the bool returned from TryParse, so all invalid integers will come first, use ThenByDescending if you want the valid first. If you want to sort by the value use for example: ThenBy(o => Int32.TryParse(o.Note, out int note) ? note : int.MinValue).
4
Documenti = Documenti.OrderBy(o =>
        int.TryParse(o.Note, out int val)
            ? val
            : int.MaxValue /* or int.MinValue */
    ).ToList();

Note: Toggling between int.MaxValue and int.MinValue will either put the empty values at the front or the end of the list.

EDIT: 2020-02-07 Using an inline out variable which was introduced in C# 7

4 Comments

@TimSchmelter The thing that isn't guaranteed to work is referencing the same dummy variable in another delegate, such as strings.Where(s => int.TryParse(s, out dummy)).Select(s => dummy). That isn't what this answer has, there is no problem here.
I agree it is probably best practice to move the temp variable inside the delegate and is pretty easy to do. I actually modified my answer to do this, but then decided to roll it back since there seems to be a good discussion on whether it is kosher to use a variable like this.
@Servy: i have only just seen these old comments. I have already edited my answer above to link to a comment of E. Lippert where he shows that not a implementation detail might change but that using int.TryParse in a LINQ query can cause other undesired effects.
@TimSchmelter That's showing that it's confusing, hard to read/understand, poor practice, easy for a future developer to accidentally break if they're refactoring the code, etc. None of that makes it relying on implementation details. I absolutely agree with using Eric's approach of wrapping this functionality in a method to hide the variable mutation behind a black box, it's simply your explanation of why that's important isn't correct.
3

You can actually put much more complex logic in the lambda expression:

List<Doc> Documenti = new List<Doc>() {
        new Doc(""),
        new Doc("1"),
        new Doc("-4"),
        new Doc(null) };

Documenti = Documenti.OrderBy(o => string.IsNullOrEmpty(o.Note)).ThenBy(o => 
{
    int result;
    if (Int32.TryParse(o.Note, out result))
    {
        return result;
    } else {
        return Int32.MaxValue;
    }
}).ToList();

foreach (var item in Documenti)
{
    Console.WriteLine(item.Note ?? "null");
    // Order returned: -4, 1, <empty string>, null
}

Remember, o => Int32.TryParse(...) is just a shorthand for creating a delegate that just takes in o as a parameter and returns Int32.TryParse(...). You can make it do whatever you want as long as it still is a syntacticly correct method with the correct signature (ex, all code paths return an int)

Comments

2

That won't produce the expected results b/c TryParse returns a bool rather than int. The easiest thing to do is create a function that returns an int.

private int parseNote(string note) 
{   
  int num;   
  if (!Int32.TryParse(note, out num)) 
  {
    num = int.MaxValue; // or int.MinValue - however it should show up in sort   
  }

  return num; 
}

call that function from your sort

Documenti = Documenti
    .OrderBy(o => parseNote(o.Note))
    .ToList();

you could do it inline too, but, i think a separate method makes the code more readable. i'm sure the compiler will inline it, if it's an optimization.

3 Comments

I'd make parseNote return int? just because it makes sense to have something that isn't an int returned as "not an int". You should then still be able to make nulls appear last, if required, by doing .OrderByDescending(o => -parseNote(o.Note)).
That won't produce the expected results b/c TryParse returns a bool rather than int It looks like he wants that; he wants to move all items that aren't ints to the end, not order them by their numeric value.
the code i submitted will sort objects by the int values and push items with non-int note values to the bottom, i thought that's what was asked for. the question is a bit vague on that front
1

C# 7 has some new features that make this even easier

var ints = from a in str.Split(',').Select(s=> new { valid = int.TryParse(s, out int i), result = i })
           where  a.valid
           select a.result;

or as you are asking specifically about sorting

var ints = from a in str.Split(',')
           orderby (int.TryParse(s, out int i) ? i : 0 )
           select a.result;

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.