Reputation: 630
I know it has been discussed a lot, but, being a beginner in LINQ I don't manage to filter this situation:
I have a list of Customers
containing a list of Projects
list to filter:
List<Customer> Customers {get; set;}
this is Customer class:
class Customer
{
public string Name {get; set;}
public List<Project> Projects {get; set;}
}
this is Project class:
class Project
{
public string Name {get; set;}
}
All I want is to find all customers containing ONLY the projects that have a certain name (my app will filter by the project's name).
As for now I have this:
Customers
.Where(c => c.Projects.Any(p => p.Name.ToLowerInvariant().Contains(lowerCaseFilter)));
but it doesn't help, because it will return all the customers containing ALL the projects not only the one I have searched.
For example, if I have a list of 15 customers with an unknown number of projects in each of them, I want to display only those customers and only those projects in every customers that meet the searched term.
I think I am missing something here...
Upvotes: 1
Views: 1151
Reputation: 2929
I am not sure what you mean by
it will return all the customers containing ALL the projects not only the one I have searched.
I can interpret that in two ways, please clarify:
Solution for (a):
Customers
.Where(c => c.Projects.Any(p => p.Name.ToLowerInvariant().Contains(lowerCaseFilter)))
.Select(c => new Customer
{
// set all other properties
Projects = c.Projects
.Where(p => p.Name.ToLowerInvariant().Contains(lowerCaseFiler))
.ToList()
});
What you do here is simply filter the Projects
collection of the result by the desired value.
Solution for (b):
Customers
.Where(c => c.Projects.All(p => p.Name.ToLowerInvariant().Contains(lowerCaseFilter)))
This simply filters the customers collection for those items that only contain projects which match the desired filter.
Upvotes: 0
Reputation: 169340
Something like this:
var customers = Customers
.Select(c => new Customer() { Name = c.Name, Projects = c.Projects == null ? new List<Project>() : c.Projects.Where(p => !string.IsNullOrEmpty(p.Name) && p.Name.ToLower().Contains(lowerCaseFilter)).ToList() })
.Where(c => c.Projects.Any())
.ToList();
You will have to create new Customer objects that only contain the projects that you want.
Upvotes: 0
Reputation: 460228
You have to recreate the customers and the projects if you want to modify the list:
var searchedCustomers = Customers
.Select(c => new {
Customer = c,
FilteredProjects = c.Projects
.Where(p => string.Equals(p.Name, nameFilter, StringComparison.InvariantCultureIgnoreCase))
.ToList()
})
.Where(x => x.FilteredProjects.Any())
.Select(x => new Customer{
Name = x.Customer.Name,
Projects = x.FilteredProjects
});
I've used String.Equals
with StringComparison.InvariantCultureIgnoreCase
to avoid creating lower case strings and to avoid some localization issues like the turkish i problem. So you also don't need the lowerCaseFilter
with this approach so i've named it nameFilter
.
Upvotes: 3