List<>.FindAll with few conditions - c#

There's some faster method than this to find all person with some conditions?
if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(lastname) && !String.IsNullOrEmpty(phone))
{
List<Person> newList = List.FindAll(s => s.Name == name && s.Surname == lastname && s.Phone == phone);
}
else if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(lastname))
{
List<Person> newList = List.FindAll(s => s.Name == name && s.Surname == lastname);
}
etc.

Your version is likely the fastest option at runtime. The List<T>.FindAll predicate you created is efficient, since it does the fewest checks.
However, you could use LINQ to make the code simpler and more maintainable:
IEnumerable<Person> people = List; // Start with no filters
// Add filters - just chaining as needed
if (!string.IsNullOrWhitespace(name) && !string.IsNullOrWhitespace(lastname))
{
people = people.Where(s => s.Name == name && s.Surname == lastname);
if (!string.IsNullOrWhitespace(phone))
people = people.Where(s => s.Phone == phone);
}
//... Add as many as you want
List<Person> newList = people.ToList(); // Evaluate at the end
This will be far more maintainable, and likely be "fast enough", since filtering is typically not done in a tight loop.

I'd rewrite this way just to make it easier to read:
if (!String.IsNullOrEmpty(name) && !String.IsNullOrEmpty(lastname)) {
if (!String.IsNullOrEmpty(phone))
{
List<Person> newList = List.FindAll(s => s.Name == name && s.Surname == lastname && s.Phone == phone);
}
else
{
List<Person> newList = List.FindAll(s => s.Name == name && s.Surname == lastname);
}
}

Related

Searching in MVC5 using linq

Using this Linq code I get any values corresponding to the search input i.e if I search country = Italy and gender = female I get both employees from Italy and employees who are female but I need it to be more specific.
i.e if I search Country = Italy and Gender = female I need to get female employees from Italy. Please suggest me a Linq code for the same
Also, I have five search inputs (First Name, Last Name, Designation, Country, Gender) so just (&&) only doesn't do the work here!
Here's the code:
List<Employee> Elist = userdb.Employees
.Where(i => i.FirstName == Fn ||
i.LastName == Ln ||
i.Designation == desig ||
i.Country == country ||
i.Gender == gender)
.ToList();
This is a situation where the nature of IQueryable comes in very useful. You can add Where clauses to your query without actually executing anything against the database. The SQL would only be executed when you materialise the data, for example using ToList(). This is called deferred query execution.
So you can write your code like this:
IQueryable<Employee> query = userdb.Employees;
if(!string.IsNullOrEmpty(Fn))
{
query = query.Where(e => e.FirstName == Fn);
}
if(!string.IsNullOrEmpty(Ln))
{
query = query.Where(e => e.LastName == Ln);
}
// etc. etc.
List<Employee> Elist = query.ToList();
Most likely, you are not wanting to include criteria that is not filled in. You would only want to filter by a value if the value exists (or is not null). Use an IQueryable to build your search and then assign it to Elist.
IQueryable<Employee> Query = userdb.Employees;
if (Fn != null) {
Query = Query.Where(i => i.FirstName.Equals(Fn));
}
if (Ln != null) {
Query = Query.Where(i => i.LastName.Equals(Ln));
}
if (desig != null) {
Query = Query.Where(i => i.Designation.Equals(desig));
}
if (country != null) {
Query = Query.Where(i => i.Country.Equals(country));
}
if (gender != null) {
Query = Query.Where(i => i.Gender.Equals(gender));
}
List<Employee> Elist = Query.ToList();
Personally I would use a PredicateBuilder here. A small example, let's say you just have 2 queries:
Expression<Func<Person, bool>> hasFirstName = p1 => p1.FirstName == Fn;
Expression<Func<Person, bool>> hasLastName= p2 => p2.LastName == Ln";
You could build this into a predicate builder like so and keep on expanding using any sort of logic:
var predicate = PredicateBuilder.False<Employee>();
if (!string.IsNullOrEmpty(Fn))
{
predicate = predicate.And(e => e.FirstName == Fn);
}
if (!string.IsNullOrEmpty(Ln))
{
predicate = predicate.And(e => e.FirstName == Ln);
}
var result = userdb.Employees.Where(predicate);
Try this
List<Employee> Elist = userdb.Employees
.Where(i => (Fn == null || i.FirstName == Fn ) &&
(Ln == null || i.LastName == Ln ) &&
(desig == null || i.Designation == desig) &&
(country == null || i.Country == country) &&
(gender == null || i.Gender == gender)
.ToList();

query with lambda expressions and one parameter is null

I want to do a query with lambda expression.
My database is a Cosmos DB.
I want to filter for two parameters and one of the two can be null.
For example i want to search for name and lastname and one of both is null.
This is that I am trying:
var result = this._client.CreateDocumentQuery<Person>(
UriFactory.CreateDocumentCollectionUri(_idDatabase, _idCollection), queryOptions)
.Where((f) => f.Name == Name && f.LastName == lastName )
.AsEnumerable()
.ToList();
So this?
var result = this._client.CreateDocumentQuery<Person>(
UriFactory.CreateDocumentCollectionUri(_idDatabase, _idCollection), queryOptions)
.Where((f) => (f.Name == Name || (f.Name == null && f.LastName != null)) && (f.LastName == lastName || (f.LastName == null && f.Name != null))
.AsEnumerable()
.ToList();
try something like this:
var result = this._client.CreateDocumentQuery<Person>(
UriFactory.CreateDocumentCollectionUri(_idDatabase, _idCollection), queryOptions)
.Where(f => f.Name == $"{Name ?? f.Name}") &&
f.LastName == $"{lastName ?? f.LastName}") )
.AsEnumerable()
.ToList();
Maybe you can work with IQueryable:
IQueryable<Person> iPerson = this._client.CreateDocumentQuery<Person>(
UriFactory.CreateDocumentCollectionUri(_idDatabase, _idCollection), queryOptions);
if(Name != null) iPerson = iPerson.Where(f => f.Name == Name);
if(lastName != null) iPerson = iPerson.Where(f => f.LastName == lastName)
return iPerson.AsEnumerable().ToList();

How to recursively call Where clause in Linq or SQL

Let's say I have the following filter parameters:
Type="Student"
School = "High"
ReferenceID = "123abc"
PaymentOnFile= "Y"
Now, I need to find 1st student based on these 4 parameters. If no students are found then I need to find them based on the 3 parameters, if no students are found then use 2 parameters, etc.
Here's my current code:
var Student = db.Students.Where(x=> x.School == School && x.Type == Type && x.ReferenceID == ReferenceID && x.PaymentOnFile == PaymentOnFile).FirstOrDefault();
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School && x.Type == Type && x.ReferenceID == ReferenceID).FirstOrDefault();
}
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School && x.Type == Type).FirstOrDefault();
}
if (Student == null)
{
Student = db.Students.Where(x=> x.School == School).FirstOrDefault();
}
return Student;
This works but it is not very efficient and ugly. What is a better way to do this? Maybe using expression trees or something else but I cannot figure it out.
SQL also works!
I think something like this would work:
var student = db.Students
.Where(x => x.School == school)
.OrderBy(x => (x.Type == type) ? 0 : 1)
.ThenBy(x => (x.ReferenceID == referenceId) ? 0 : 1)
.ThenBy(x => (x.PaymentOnFile == paymentOnFile) ? 0 : 1)
.FirstOrDefault();
This dynamic solution will perform exactly your recursive logic and will work at case of Linq and EF. You can add another conditions(to predicates, order matters), solution(for loop) will remain the same.
var predicates = new List<Expression<Func<Student, bool>>>
{
x => x.School == "High",
x => x.Type == "Student",
x => x.ReferenceID == "123abc",
x => x.PaymentOnFile == "Y",
};
Student student = null;
for(var i = 0; i < predicates.Count; i++)
{
var query = db.Students.AsQueryable();
for (var j = 0; j < predicates.Count - i; j++)
query = query.Where(predicates[j]);
if ((student = query.FirstOrDefault()) != null)
break;
}
i guess if you could apply this query logic which can help you better
SELECT TOP 1
*
FROM Student AS S
ORDER BY CASE
WHEN S.School = #School AND S.[Type] = #Type AND S.ReferenceID = #ReferenceID AND S.PaymentOnFile = #PaymentOnFile THEN
1
WHEN S.School = #School AND S.[Type] = #Type AND S.ReferenceID = #ReferenceID THEN
2
WHEN S.School = #School AND S.[Type] = #Type THEN
3
WHEN S.School = #School THEN
4
END ASC

MVC4 Linq Query Optimisation

I have the below code which works but I do not feel this is the best way to achieve the result. I am looking at optimising my code. Any suggestions of a better option will be appreciated. sub is a subcategory which is nullable.
[AllowAnonymous]
public ActionResult _relatedgrps(string cat, string sub)
{
if (!string.IsNullOrWhiteSpace(sub)){
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat && u.SubCategory.CatName == sub)
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}else{
var pgs = db.Pages
.Where(u=>u.MetaNoSearch==false)
.Where(u => u.PaOk == true && u.Category.Name == cat )
.OrderByDescending(u => u.PaCreatedOn);
return PartialView(pgs.ToList());
}}
Linq IEnumerables can be additive and the query will only be executed when enumerated for the first time (like calling .ToList()). So you should be able to do something like this:
var pgs = db.Pages
.Where(u => u.MetaNoSearch == false &&
u.PaOk == true &&
u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
return PartialView(pgs.OrderByDescending(u => u.PaCreatedOn).ToList());
Create an object to query it. To improve it, you also could remove it boolean comparations because they are conditions.
var query = db.Pages.Where(u => !u.MetaNoSearch && u.PaOk && u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
query = query.Where(u => u.SubCategory.CatName == sub);
query = query.OrderByDescending(u => u.PaCreatedOn);
return PartialView(query.ToList());
#user3021830 - be careful with String.IsNullOrWhitespace, you cannot use that in a database query. You could do String.IsNullOrWhitespace(sub), but not String.IsNullOrWhitespace(u.*).
I'd avoid any conditionals in the query because that will likely result in a case statement in the SQL.
To produce the best SQL I'd do something like this:
var pgs = db.Pages.Where(u => u.MetaNoSearch == false)
.Where(u => u.PaOk == true)
.Where(u => u.Category.Name == cat);
if (!string.IsNullOrWhiteSpace(sub))
{
pgs = pgs.Where(u => u.SubCategory.CatName == sub);
}
var result = pgs.OrderByDescending(u => u.PaCreatedOn).ToList();

Refactoring LINQ query

Consider the below
if(type== "S")
{
lstItem.ItemsSource = (from item in Items
where item.Property1 == "SomeValue"
select item);
}
else
{
lstItem.ItemsSource = (from item in Items
where item.Property2 == "SomeOtherValue"
select item);
}
As can be figured out that the only difference between these two queries is only in the property name (for the first one it is Property1 & for the second it is Property2)
Is there any better way of refactoring / writing the code in a structured mannner(some common method where only the property name will be passed and the record will be filtered as per that) or this is the proper way of doing the same?
Need help.
Thanks
It is also possible to add an inline if in the where clause
lstItem.ItemsSource =
(from item in Items
where (test == "S" ? item.Property1 == "SomeValue" : item.Property2 == "SomeOtherValue")
select item);
You can chain your commands within if statements. E.g.:
var items = from item in Items
select item;
if(type== "S")
{
items = items.Where(item => item.Property1 == "SomeValue");
}
else
{
items = items.Where(item => item.Property2 == "SomeOtherValue");
}
Or even just write the tidier lambda structure in you orignal code:
if(type== "S")
{
lstItem.ItemsSource = Items.Where(item => item.Property1 == "SomeValue");
}
else
{
lstItem.ItemsSource = Items.Where(item.Property2 == "SomeOtherValue");
}
I like:
lstItem.ItemsSource = Items.Where(type == "S" ?
item => item.Property1 == "SomeValue":
item => item.Property2 == "SomeOtherValue");
well, you could start by boiling the expression down to:
Func<Items, bool> expr;
if(type== "S")
{
expr = (item => item.Property1 == "SomeValue");
}
else
{
expr = (item => item.Property2 == "SomeOtherValue");
}
var items = Items.Where(expr);
of course, the game plan is really to make it all a single statemnet, but this makes it a LITTLE more manageable i think :)
jim

Categories

Resources