A sort of:
Documenti = Documenti
.OrderBy(o => string.IsNullOrEmpty(o.Note))
.ThenBy(o => Int32.TryParse(o.Note))
.ToList();
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();
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
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;
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
)
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.