I have an integer column(not null) in a sql server 2008 database/table, I want to retrieve it.
// go to the table and get the largest number, if none exists, use 0.
int iNumber = iContext.DetailsRecords.Max(x => x.Number); // from entity
But at the very beginning, the table is empty. I want to set is as 0.
How to change the code?
If you don't want to check if the DetailsRecords is null, but rather handle if the source sequence is empty, then use this answer (I adjusted it a bit differently for your LINQ usage):
Max or Default?
int iNumber = iContext.DetailsRecords.Select(x => (int?)x.Number).Max() ?? 0;
Enumerable.Max Method (IEnumerable<Int32>): InvalidOperationException -
source contains no elements.
Enumerable.Max Method (IEnumerable<Nullable<Int32>>): If the source sequence is empty or contains only values that are null, this function returns null.
You can use DefaultIfEmpty for this. If the sequence is empty it will return the provided item as the only item in the sequence, if not it will return the original sequence.
IEnumerable<int> numbers = new int [] { };
numbers.DefaultIfEmpty(0).Max();
try this:
int iNumber = iContext.DetailsRecords==null? iContext.DetailsRecords.Max(x => x.Number) : 0;
or
int iNumber = iContext.DetailsRecords.Any()? iContext.DetailsRecords.Max(x => x.Number) : 0; if table is not null and contains 0 records.
Use the Any function to see if there are any records:
int iNumber = iContext.DetailsRecords.Any()
? iContext.DetailsRecords.Max(x => x.Number)
: 0;
I know this already has an accepted answer, but another good way would just be FirstOrDefault().
int iNumber = iContext.DetailsRecords.OrderByDescending(x => x.Number).FirstOrDefault();
I use this way often and also can let you setup a new object if the result was null.
MyObject m = mySearch.GetItems.Where(a => a.id == id).FirstOrDefault() ?? new MyObject();
Related
I need to check using Linq if I have the same number of times in the database for the ZPZ_Von column and ZPZ_Bis for the same date.
I thought I did it this way, but still after setting up a breik point it appears to me in 4 values. Please correct me where I make a mistake.
var ZPZ_VON = from n in arrivals where n.ZPZ_Von != null select n;
var ZPZ_BIS = from n in arrivals where n.ZPZ_Bis != null select n;
Data like this in the database look like this.
Because DateTime is a struct, it cannot be null. So comparing the values to null is useless, because they will never be null, but you can however compare them to the default() of the DateTime struct, as they might be that.
I'll be using Lambda Expressions instead of Query Expressions, as I just like them more and they're C# code instead of pseudo-sql
ZPZ_VON = n.Where(x => x.ZPZ_Von != default(DateTime)).ToList();
ZPZ_BIS = n.Where(x => x.ZPZ_Bis != default(DateTime)).ToList();
Note: I removed the x.ZPZ_VON != null I had in my comment as it is superfluous and I'd edit comment but it has been more than 5 minutes
You didn't provide your model but I suspect you have the ZPZ_Von setup as a DateTime.
Based off the OP the proper setup would be to have ZPZ_Von as a nullable: DateTime?
public class Arrival
{
public DateTime? ZPZ_Von { get; set;}
}
And you can then check for null in your query like this:
n.Where(x => x.ZPZ_Von.HasValue).ToList();
In short, this makes your model reflect the actual db setup and will save you headaches in the long run.
You should try this
var ZPZ_VON = from n in arrivals where n.ZPZ_Von is not null select n;
var ZPZ_BIS = from n in arrivals where n.ZPZ_Bis is not null select n;
when I use someList.Where(t => t.isTrue = true) nothing happens. But when I use code as given below,
if(someList.Where(t => t.isTrue = true).Count() > 0)
return;
All items inside the list are set to true. Why this is happening?
Edit : I am not trying to assign or compare anything. I am curious about why this happens when used with if.
This happens because you use an assignment instead (=) of equality compare (==).
Also it only happens when you use Count because LINQ only evaluates the lambda expression when it has to get a value.
var q = someList.Where(t => t.isTrue = true); // Nothing will happen
q.ToList() // would happen here
if(q.Count() > 0 ) { .. } // Also here
To compare and not assign the value you should use:
var q = someList.Where(t => t.isTrue == true);
var q = someList.Where(t => t.isTrue); // Or simpler
The reason the compiler allows this is because assignment is an expression that has a value. For example :
int a = 10;
int b;
int c = (b = a) ; // (a=b) is of type int even though it also assigns a value, and b and c will have a value of 10
In your case, the assignment of a bool has type bool, which happens to be a valid return value for a lambda passed to Where
All items inside the list are set to true when you use = and then evaluates the expression by using Count().
As the isTrue is a boolean this would be enough to count the values which is true
if(someList.Where(t => t.isTrue).Count() > 0)
return;
As an alternative to checking if the count is higher than 0 you can use the Any method which already does just that
if(someList.Where(t => t.isTrue).Any()) // Any returns true if there are any elements in the collection
return;
You can further simplify this with an overload of Any that takes the condition as a parameter, skiping the additional Where
if(someList.Any(t => t.isTrue)) // This overload takes a function that returns a boolean like Where does and returns true if there is at least 1 element that matches the condition
return;
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?
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
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.
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;
I have the following Linq query:
result.Partials.Where(o => o.IsPositive).Min(o => o.Result)
I get an exception when result.Partials.Where(o => o.IsPositive) does not contains elements. Is there an elegant way to handle this other than splitting the operation in two and checking for null? I have a class full of operations like this one.
EDIT: The question is related with LINQ to Objects.
This is the Exception I'm getting (translated it says: The sequence is empty):
A short summary of the calculation of a Min
- No mediation (Exception!)
var min = result.Partials.Where(o => o.IsPositive).Min(o => o.Result);
This is your case: if there are no matching elements, then the Min call will raise an exception (InvalidOperationException).
- With DefaultIfEmpty() -- still troublesome
var min = result.Partials.Where(o => o.IsPositive)
.Select(o => o.Result)
.DefaultIfEmpty()
.Min();
DefaultIfEmpty will create an enumeration over the 0 element, when there are no elements in the list. How do you know that 0 is the Min or if 0 stands for a list with no elements?
- Nullable values; A better solution
var min = result.Partials.Where(o => o.IsPositive)
.Min(o => (decimal?)o.Result);
Here Min is either null (because that's equal to default(decimal?)) or the actual Min found.
So a consumer of this result will know that:
When result is null then the list had no elements
When the result is a decimal value then the list had some elements and the Min of those elements is that returned value.
However, when this doesn't matter, then min.GetValueOrDefault(0) can be called.
You can use the DefaultIfEmpty method to ensure the collection has at least 1 item:
result.Partials.Where(o => o.IsPositive).Select(o => o.Result).DefaultIfEmpty().Min();
You can't use Min (or Max) if the sequence is empty. If that shouldn't be happening, you have a different issue with how you define result. Otherwise, you should check if the sequence is empty and handle appropriately, eg:
var query = result.Partials.Where(o => o.IsPositve);
min = query.Any() ? query.Min(o => o.Result) : 0; // insert a different "default" value of your choice...
Yet another way to express it in LINQ is to use Aggregate:
var min = result.Partials
.Where(o => o.IsPositive)
.Select(o => o.Result)
.Aggregate(0, Math.Min); // Or any other value which should be returned for empty list
Since LINQ lacks methods like MinOrDefault() and MaxOrDefault(), you can create them by yourself:
public static class LinqExtensions
{
public static TProp MinOrDefault<TItem, TProp>(this IEnumerable<TItem> This, Func<TItem, TProp> selector)
{
if (This.Count() > 0)
{
return This.Min(selector);
}
else
{
return default(TProp);
}
}
}
Therefore, if the collection has values, the Min() is calculated, otherwise you get the property type's default value.
An example of use:
public class Model
{
public int Result { get; set; }
}
// ...
public void SomeMethod()
{
Console.WriteLine("Hello World");
var filledList = new List<Model>
{
new Model { Result = 10 },
new Model { Result = 9 },
};
var emptyList = new List<Model>();
var minFromFilledList = filledList.MinOrDefault(o => o.Result)); // 9
var minFromEmptyList = emptyList.MinOrDefault(o => o.Result)); // 0
}
NOTE 1: you don't need to check if the This parameter is null: the invoked Count() already checks that, and it throws the same Exception that you would throw.
NOTE 2: This solution is good only in situations where the Count() method is cheap to call. All .NET collections are fine since they are all very efficient; it could be a problem for particular customized/non-standard collections.
I have the following ItemArray:
dt.Rows[0].ItemArray.. //{0,1,2,3,4,5}
the headers are : item0,item1,item2 etc..
So far, to get a value from the ItemArray I used to call it by an index.
Is there any way to get the value within the ItemArray with a Linq expression based on the column name?
Thanks
You can also use the column-name to get the field value:
int item1 = row.Field<int>("Item1");
DataRow.Item Property(String)
DataRow.Field Method: Provides strongly-typed access
You could also use LINQ-to-DataSet:
int[] allItems = (from row in dt.AsEnumerable()
select row.Field<int>("Item1")).ToArray();
or in method syntax:
int[] allItems = dt.AsEnumerable().Select(r => r.Field<int>("Item1")).ToArray();
If you use the Item indexer rather than ItemArray, you can access items by column name, regardless of whether you use LINQ or not.
dt.Rows[0]["Column Name"]
Tim Schmelter's answer is probably what you are lookin for, just to add also this way using Convert class instead of DataRow.Field:
var q = (from row in dataTable.AsEnumerable() select Convert.ToInt16(row["COLUMN1"])).ToArray();
Here's what I've come up with today solving a similar problem. In my case:
(1)I needed to xtract the values from columns named Item1, Item2, ... of bool type.
(2) I needed to xtract the ordinal number of that ItemN that had a true value.
var itemValues = dataTable.Select().Select(
r => r.ItemArray.Where((c, i) =>
dataTable.Columns[i].ColumnName.StartsWith("Item") && c is bool)
.Select((v, i) => new { Index = i + 1, Value = v.ToString().ToBoolean() }))
.ToList();
if (itemValues.Any())
{
//int[] of indices for true values
var trueIndexArray = itemValues.First().Where(v => v.Value == true)
.Select(v => v.Index).ToArray();
}
forgot an essential part: I have a .ToBoolean() helper extension method to parse object values:
public static bool ToBoolean(this string s)
{
if (bool.TryParse(s, out bool result))
{
return result;
}
return false;
}