I want to convert this code to a linq solution. What it does it looks into a collection of customers and see if at least one of the has a middle name. This code works fine, I'm just trying to learn linq, so looking for an alternative solution.:
//Customers - List<Customer>
private bool checkMiddleName()
{
foreach (Customer i in Customers)
{
if (i.HasMiddleName == true)
{
return true;
}
}
return false;
}
I tried to write something like: (Customers.Foreach(x=>x.HasMiddleName==true)...
but looks line it's not the method I'm looking for.
If you just want to know if theres at least one, you can use Enumerable.Any:
bool atLeastOneCustomerWithMiddleName = Customers.Any(c => c.HasMiddleName);
If you want to know the first matching customer, you can use Enumerable.First or Enumerable.FirstOrDefault to find the first customer with MiddleName==true:
var customer = Customers.FirstOrDefault(c => c.HasMiddleName);
if(customer != null)
{
// there is at least one customer with HasMiddleName == true
}
First throws an InvalidOperationException if the source sequence is empty, whereas FirstOrDefault returns null if there's no match.
var result = Customers.Where(x=>x.HasMiddleName == true).FirstOrDefault();
Based on this:
What it does it looks into a collection of customers and see if at least one of the has a middle name.
Try
return Customers.Where(x => x.HasMiddleName).Any();
This query return true if at least one custmer has the property HasMiddleName = true
You may use the following to achieve what you need:
Customers.Where(cust=> cust.HasMiddleName).Count > 0
So , if Count is more than zero means you have some customers who have Middle name.
Or for better performance you may say:
bool customerWithMiddleName;
foreach(Customer cust in Customers)
{
if(cust.HasMiddleName)
{
customerWithMiddleName = true;
break;
}
}
Related
This is my code to find string in my UserTable.
var PhoneExists = _db.UserTable.Where(u => u.PhoneNumber == Input.PhoneNumber);
and this is my if code:
if (PhoneExists != null)
{
ErrorAlert("number found.");
return Page();
}
but every time the if condition is true.
It returns true as PhoneExists is not null, but it returns with IQueryable value.
You need to materialize the query to return the result queried from the database.
You may look for .Any() to find whether there is any record with the respective PhoneNumber existed.
bool PhoneExists = _db.UserTable.Any(u => u.PhoneNumber == Input.PhoneNumber);
if (PhoneExists)
{
...
}
Where will always return a non-null enumerable, and it's empty if there are no matches. You have to check its contents. If you expect the phone numbers to be unique, you could use FirstOrDefault or SingleOrDefault to return either a string, or null. The latter will, however, throw if there is more than one match.
You can use Contains here.
Sample code:
var PhoneExists = _db.UserTable.Where(u => u.PhoneNumber.Contains(Input.PhoneNumber)).FirstOrDefault();
In your if code:
if (PhoneExists != null && PhoneExist.Count()== 0)
{
ErrorAlert("number found.");
return Page();
}
You can use Exists instead of Where like this :
if (_db.UserTable.Exists(u => u.PhoneNumber == Input.PhoneNumber))
{
//Do Something
}
About exist checkout https://learn.microsoft.com/tr-tr/dotnet/api/system.collections.generic.list-1.exists?view=net-6.0
You can use FirstOrDefault()
var PhoneExists = _db.UserTable.Where(u => u.PhoneNumber == Input.PhoneNumber).FirstOrDefault();
I have a datatable loaded up with some records and I am then pulling a query from another file and want to check if the ID that I pull in this query exists in my datatable.
foreach (var item in records)
{
bool hasit = dt.AsEnumerable().Any(p => p.Field<string>(0) == item.ID);
if (!hasit)
{
//Logic
}
}
I'm using that .Any() function and expecting it to return true if there is an ID in the first field of the datatable that matches the id in the records collection. It continually returns false though, am I missing something? Is there a better way to do this?
I'm using that .Any() function and expecting it to return true if there is an ID in the first field of the datatable that matches the id in the records collection. It continually returns false
When one uses == it compares object references. I recommend you instead use Equals which will just compare the values. Hence change your statement to
dt.AsEnumerable().Any(p => p.Field<string>(0).Equals(item.ID))
Which will achieve what you are expecting.
The method
.Any(p => p.Field(0) == item.ID)
will return true IF ANY element is found. Your posted code specifies that the next thing you do is ask
if (!hasit)
{
//Logic
}
which means if(NOT any has it)... which is producing the incorrect behavior. Change it to:
if (hasit)
{
//Logic
}
and you'll get the desired results.
Edit: kudos to Cuong Le for the observation.
I would try breaking it up to see if I could find the error:
foreach (var item in records)
{
var enumer = dt.AsEnumerable(); // <-- Check this to make sure it has elements
var filtered = enumer.Any(p => p.Field<string>(0) == item.ID); // <- Check to make sure it has elements
}
I have a piece of code that's performing badly, and need to rewite it to introduce a proper where clause before starting the .ToList however, that's where I'm getting stuck.
Currently the code looks lke this (roughly, I've taken some of the search criteria out to make it easier to display)
var Widgets = from b in _caspEntities.Widgets.Include("WidgetRegionLogs")
.Include("WidgetStatusLogs").Include("WidgetVoltageTests")
select b;
IEnumerable<Widget> results = Widgets.ToList();
if (comboBoxRegion.SelectedValue.ToString() != "0")
{
results = from b in results
where b.CurrentRegionLog != null && b.CurrentRegionLog.RegionId == int.Parse(comboBoxRegion.SelectedValue.ToString())
select b;
}
if (comboBoxStatus.SelectedValue != null)
{
results = from b in results
where b.CurrentStatusLog != null && b.CurrentStatusLog.StatusId == comboBoxStatus.SelectedValue.ToString()
select b;
}
if (txtCode.Text.Trim().Length > 0)
{
results = from b in results
where b.CodeNumber == txtCode.Text.Trim()
select b;
}
dataGridViewWidget.DataSource = results.ToList();
I can write the SQL easily enough, essentially the model is simple, I have a Widget it has a RegionLog and a StatusLog, both of which store a history. The current region and status are retrieved from this by grouping by WidgetID and selecting the most recent Date Updated (and then going off to Region and Status tables to get the actual value).
So, I need to translate this into LINQ, but to be honest I don't have a clue but am ken and willing to learn. In my head, I think I need to add some better where clauses, and then do the Widget.toList after I have applied the where clauses. I'm struggling with the CurrentRegionLog and CurrentStatusLog concepts as they are not populated until I run the IEnumerable.
If anyone can give some pointers, I'd be grateful,
Thanks
Edit - Added
public BatteryRegionLog CurrentRegionLog
{
get { return _currentRegionLog; }
}
private BatteryRegionLog _currentRegionLog
{
get
{
if (this.BatteryRegionLogs.Count > 0)
{
BatteryRegionLog log = this.BatteryRegionLogs.OrderByDescending(item => item.LastModifiedDate).First();
return log;
}
else
{
return null;
}
}
}
You can compose the query like this:
if (comboBoxRegion.SelectedValue.ToString() != "0")
{
var id = int.Parse(comboBoxRegion.SelectedValue.ToString()
Widgets = from b in Widgets
let currentRegionLog =
b.BatteryRegionLogs
.OrderByDescending(item => item.LastModifiedDate)
.FirstOrDefault()
where currentRegionLog.RegionId == id)
select b;
}
... // Same for the other criteria.
dataGridViewWidget.DataSource = Widgets.ToList();
The whole query is not executed before you do ToList(). As everything is translated to SQL you don't need the null check b.CurrentRegionLog != null. SQL will evaluate b.CurrentRegionLog.RegionId == id just fine when there is no CurrentRegionLog.
Edit
Since CurrentRegionLog is a calculated property of your Widget class it cannot be translated into SQL. I made an effort to incorporate the code of calculated property into the query in a way that only the basic navigation property is used, so EF can translate it to SQL again.
try remove this line:
IEnumerable<Widget> results = Widgets.ToList();
and just use the Widgets variable you get in at the top
The .ToList() goes to the database and materialiaze all the data into entities.
if you don't call the .ToList() the query is still "open" for a where clause
Here is some sample code I have basically written thousands of times in my life:
// find bestest thingy
Thing bestThing;
float bestGoodness = FLOAT_MIN;
foreach( Thing x in arrayOfThings )
{
float goodness = somefunction( x.property, localvariable );
if( goodness > bestGoodness )
{
bestGoodness = goodness;
bestThing = x;
}
}
return bestThing;
And it seems to me C# should already have something that does this in just a line. Something like:
return arrayOfThings.Max( delegate(x)
{ return somefunction( x.property, localvariable ); });
But that doesn't return the thing (or an index to the thing, which would be fine), that returns the goodness-of-fit value.
So maybe something like:
var sortedByGoodness = from x in arrayOfThings
orderby somefunction( x.property, localvariable ) ascending
select x;
return x.first;
But that's doing a whole sort of the entire array and could be too slow.
Does this exist?
This is what you can do using System.Linq:
var value = arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable))
.First();
If the array can be empty, use .FirstOrDefault(); to avoid exceptions.
You really don't know how this is implemented internally, so you can't assure this will sort the whole array to get the first element. For example, if it was linq to sql, the server would receive a query including the sort and the condition. It wouldn't get the array, then sort it, then get the first element.
In fact, until you don't call First, the first part of the query isn't evaluated. I mean this isn't a two steps evaluation, but a one step evaluation.
var sortedValues =arrayOfThings
.OrderByDescending(x => somefunction(x.property, localvariable));
// values isn't still evaluated
var value = sortedvalues.First();
// the whole expression is evaluated at this point.
I don't think this is possible in standard LINQ without sorting the enuermable (which is slow in the general case), but you can use the MaxBy() method from the MoreLinq library to achieve this. I always include this library in my projects as it is so useful.
http://code.google.com/p/morelinq/source/browse/trunk/MoreLinq/MaxBy.cs
(The code actually looks very similar to what you have, but generalized.)
I would implement IComparable<Thing> and just use arrayOfThings.Max().
Example here:
http://msdn.microsoft.com/en-us/library/bb347632.aspx
I think this is the cleanest approach and IComparable may be of use in other places.
UPDATE
There is also an overloaded Max method that takes a projection function, so you can provide different logic for obtaining height, age, etc.
http://msdn.microsoft.com/en-us/library/bb534962.aspx
I followed the link Porges listed in the comment, How to use LINQ to select object with minimum or maximum property value and ran the following code in LINQPad and verified that both LINQ expressions returned the correct answers.
void Main()
{
var things = new Thing [] {
new Thing { Value = 100 },
new Thing { Value = 22 },
new Thing { Value = 10 },
new Thing { Value = 303 },
new Thing { Value = 223}
};
var query1 = (from t in things
orderby GetGoodness(t) descending
select t).First();
var query2 = things.Aggregate((curMax, x) =>
(curMax == null || (GetGoodness(x) > GetGoodness(curMax)) ? x : curMax));
}
int GetGoodness(Thing thing)
{
return thing.Value * 2;
}
public class Thing
{
public int Value {get; set;}
}
Result from LinqPad
I have a class named Country. It has the public members, 'CountryName' and 'States'.
I have declared a list of Countries.
Now I want to write a function which accepts a new 'Country' and decides if CountryList already has the 'Country'.
I tried writing a function like
bool CheckCountry(Country c)
{
return CountryList.Exists(p => p.CountryName == c.Name
&& p.States.SequenceEqual(c.States));
}
As I want to compare States using the CountryName property of the States, I want to modify my function so that SequenceEqual works based on the CountryName of the states?
Break it down into many simple queries, and then put those queries back together.
Let's start by making a sequence of items that match by name:
var nameMatches = from item in itemList where item.Name == p.Name select item;
We need to compare those items against the sequence of names in p's subitems. What's that sequence? Write a query:
var pnames = from subitem in p.SubItems select subitem.Name;
Now you want to find all the elements from nameMatches where the sequence of names matches. How are you going to get the sequence of names? Well, we just saw how to do that with pnames, so do the same thing:
var matches = from item in nameMatches
let subitemNames =
(from subitem in item.SubItems select subitem.Name)
where pnames.SequenceEqual(subitemNames)
select item;
And now you want to know, are there any matches?
return matches.Any();
That should work just fine as is. But if you want to be really buff you can write the whole thing in one big query!
return (
from item in itemList
let pnames =
(from psubitem in p.SubItems select psubitem.Name)
let subitemNames =
(from subitem in item.SubItems select subitem.Name)
where item.Name == p.Name
where pnames.SequenceEqual(subitemNames)
select item).Any();
And you're done. Easy as pie! Just remember, break it down into small steps, solve each problem individually, and then put the solution together out of the small results.
Have you looked at implementing IComparer on Item?
If i understand you correctly you want a way to compare two items in which you first check the name of the two items then sequentially check each subitem's name. Here's what you want:
public override bool Equals(object obj)
{
return this.Name == (obj as Item).Name;
}
public override int GetHashCode()
{
return this.Name.GetHashCode();
}
public bool Check(Item obj)
{
if (this.Name != obj.Name)
return false;
//if the lists arent of the same length then they
//obviously dont contain the same items, and besides
//there would be an exception on the next check
if (this.SubItems.Count != obj.SubItems.Count)
return false;
for (int i = 0; i < this.SubItems.Count; i++)
if (this.SubItems[i] != obj.SubItems[i])
return false;
return true;
}