Why does using LINQ change properties when used in if - c#

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;

Related

Why did I get an exception "Cannot implicitly convert type 'bool' to 'long?'" when using the LINQ Sum method and how to fix it?

I have the following code which is working:
IEnumerable<Decimal?> values = getValues();
var sum = values.Where(x => x > 0).Sum();
But if I try:
var sum = values.Sum(x => x > 0);
I get the error:
Cannot implicitly convert type 'bool' to 'long?'
Shouldn't this work either applying the filter in Sum or Where?
This code:
var sum = values.Sum(x => x > 0);
Includes a lambda expression (anonymous function) that returns a bool (true or false). It's the same as:
var sum = values.Sum(x => Check(x));
private bool Check(long x)
{
return x > 0;
}
As you cannot sum a bool the code does not work. Sum() if passed a parameter expects that parameter to be an enumeration of numbers (int or long for example, though it has MANY overloads).
Indeed, Sum requires numbers values and not boolean evaluation results.
You need first to filter using Where or Select (not relevant here) then Sum:
var sum = values.Where(x => x != null && x > 0).Sum();
I added the null check because the collection is type of decimal?.
Else you need to write that but this is less speed optimized:
var sum = values.Sum(x => x != null && x > 0 ? x : 0);
Using a selector for Sum is for example usefull when having classes, structs or tuples:
var sum = controls.Sum(control => control.Width);
As you can see here Enumerable.Sum does not have an overload which accepts a boolean returning Func as an argument. It only has overloads accepting number returning delegates.
How's that useful?
Let's say you have a list of items in the cart and you want to count the total price, you would do it like this:
var totalPrice = cartItems.Sum(ci => ci.Price);
If you want to filter it first, you have to use Where.

Linq Query to check if the records are all the same

I am very much new to the Linq queries. I have the set of records in the csv which is like below
ProdID,Name,Color,Availability
P01,Product1,Red,Yes
P02,Product2,Blue,Yes
P03,Product1,Yellow,No
P01,Product1,Red,Yes
P04,Product1,Black,Yes
I need to check for the Names of the each product and if its is not the same in all the records then I need to send an error message.I know the below query is used to find the duplicates in the records but not sure how can I modify it check if it all has the same values.
ProductsList.GroupBy(p => p.Name).Where(p => p.Count() > 1).SelectMany(x => x);
var first = myObjects.First();
bool allSame = myObjects.All(x=>x.Name == first.Name);
Enumerable.All() will return true if the lambda returns true for all elements of the collection. In this case we're checking that every object's Name property is equal to the first (and thus that they're all equal to each other; the transitive property is great, innit?). You can one-line this by inlining myObjects.First() but this will slow performance as First() will execute once for each object in the collection. You can also theoretically Skip() the first element as we know it's equal to itself.
if I understand correctly you want to check if product exists in the list
using System.Linq;
private bool ItemExists(string nameOfProduct) {
return ProductsList.Any(p=> p.Name== nameOfProduct);
}
UPD after author comment:
To know all the records that are not having the same name as the first record:
var firstName = ProductsList[0].Name;
var differentNames = ProductsList.Where(p => p.Name != firstName);
Another option (just to have all other names): ProductsList.Select(p => p.Name).Where(n => n != firstName).Distinct()
Old version
So, if there are at least two different names then you should return an error?
LINQ way: return ProductsList.Select(p => p.Name).Distinct().Count() <= 1
More optimizied way:
if (ProductsList.Count == 0)
return true;
var name = ProductsList[0].Name;
for (var i = 1; i < ProductsList.Count; i++)
{
if (ProductsList[i].Name != name)
return false;
}
return true;

c# list select unexpected (for me) behaviour

1. var test = new List<foo>() { new foo { prop1 ="1prop1", prop2 = "1prop2" }, new foo { prop1 = "2prop1", prop2 = "2prop2" } };
2. var test2 = test.Select(x => x.prop1 = "changed");
3. var test3 = test2.First();
Please, explain this behaviour to me.
Why foo.prop1 values change after line 3?
This is to do with deferred execution. Most linq methods defer execution until the resulting enumerable is actually enumerated. So when you run the Select statement it just creates an Enumerable that is ready to run the appropriate selector.
When you call First on the enumerable it runs the transform on the first item, thus changing its value.
This all assumes that you intended to write x.prop1 = "changed" and not x.prop1 == "changed". The former is the assignment operator which sets the value of x.prop1 and returns the set value. The latter is the equality operator and will return a boolean based on whether they are equal or not.
= is assignment, which means it actually changes values.
You want to use == instead, to check for equality.
Try:
var test2 = test.Select(x => x.prop1=="changed");
You are making an assignment = when you probably want to make an equality comparison ==.
var test2 = test.Select(x => x.prop1 == "changed");

Inside of a lamba expression trying to divide two values and compare to another value

C# - Inside of a lambda expression trying to divide two values and compare to another value, after checking that they are not equal to null. Doesn't seem to work in lambda expression but works in (what I think ) is equivalent foreach expression. By work I mean none of the test data satisfies the expression; the behavior of foreach code represents this (value of found after foreach code executes remains false, however, the list in the lambda expression will contain elements after code executes). I am kinda new to lambda and linq expressions, what is going on here?
linq expression is as follows:
IEnumerable<DataModel> list = dataList.Where(t => t.A != null
&& t.B != null
&& t.Compute1.Value.CompareTo((t.B.Value / t.A.Value)) != 0);
This does work in following foreach statement:
var found = false;
foreach (var data in dataList)
{
if (data.A == null || data.B == null )
{
continue;
}
var value = (double)data.B.Value / data.A.Value;
if (data.Compute1.Value.CompareTo(value) != 0)
{
found = true;
}
}
Update: I fixed this issue by casting t.Compute1, t.A, and t.B to double even though VS tells me cast is redundant.
Those are not equivalent pieces of code. Your foreach loop stops searching the moment it finds a data element where either A or B is null. Your lambda checks all the elements even if a previous one had A == null or B == null. The closest foreach equivalent would be to replace the break with a continue.
var found = false;
foreach (var data in dataList)
{
if (data.A == null || data.B == null )
{
continue;
}
var value = (double)data.B.Value / data.A.Value;
if (data.Compute1.Value.CompareTo(value) != 0)
{
found = true;
}
}

compare none value by linq

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();

Categories