I have a list of names like so:
"John"
"Alan"
and a list of People that have all sorts of names
I can search through my people table for any names that match my list like this:
people = people.Where(x => names.Contains(x.Name));
However, this performs an exact match. How do I modify my query to be able to do a LIKE search on items in the names list?
Ie, I would like my original names list to find people in the people table that have names like
"John Smith"
"Bob Alanson"
etc.
You can use Any and Contains in which you already know how to use it.
var names = new List<string>() { "John", "Alan" };
var people = new List<string>() { "John Smith", "Bob Alanson" };
var result = people.Where(x => names.Any(y => x.Contains(y))).ToList();
The result is
John Smith
Bob Alanson
You can do that with Any and string.Contains
people = people.Where(x => names.Any(n => x.Name.Contains(n)));
You can use the following query:
people = people.Where(x => names.Like(x.Name, "%key%"));
Using Contains is close to Like (%pattern%). And some "Tom Johnson" will be selected to result collection. I prefer to use StartsWith (which implements Like(pattern%)) for filtering by name.
Related
I need to write a LINQ where clause where it should satisfy the condition of multiple records.
For example, in the Animal table. I have 3 columns
Id, Name, age
I need to find all Animals where the Name is an array. ['Ant', 'Mouse', 'Turtle']
var aniList= db.Animals.Where(c =>
c.Name== arrAni[0] ||
c.Name== arrAni[1] ||
c.Name== arrAni[2] );
However, the above is hardcoded. I need to directly insert the List into the where clause so it gets filtered out. How can I do this ?
Well, you could simply use Linq .Contains method:
var aniList = db.Animals.Where(c => arrAni.Contains(c.Name));
Use Enumerable.Contains
string[] animalNames = new string[] {"ant", "mouse", "turtle"}
var desiredAnimals = animalList
.Where(animal => animalNames.Contains(animal.Name));
In words: from the collection of Animals in animalList, keep only those Animals whose Name is in the collection of AnimalNames
Basically I have a list of objects. Let's call them meetings. Inside the meetings there is another list of objects. Let's call those participants. I want to return all meetings where a certain participant is in the list.
I want something like this:
meetings.Where(meeting => meeting.Participants.Name == "Test name").ToList();
Basically return a list of meetings, where the meeting has a participant with the name "Test name".
EDIT: I actually ended up using a MongoDB filter. Before I would just extract all the "meetings" (with a filter) and then use LINQ to filter the list. Might as well filter out the results on database level.. But this is good to know.
Are you looking for Any?
var result = meetings
.Where(meeting => meeting
.Participants
.Any(participant => participant.Name == "Test name"))
.ToList();
You can use LINQ method Any and this one line of code :
var result = meetings.Where(m => m.Participants.Any(p => p.Name == "Test name")).ToList();
Even you try the following:
meetings.Where(m => m.Participants.Any(k => k.Name == "Test Name")).ToList();
I hope this is not a duplicate but I wasn't able to find an answer on this.
It either seems to be an undesired behavior or missing knowledge on my part.
I have a list of platform and configuration objects. Both contains a member string CodeName in it.
The list of CodeNames look like this:
dbContext.Platforms.Select(x => x.CodeName) => {"test", "PC", "Nintendo"}
dbContext.Configurations.Select(x => x.CodeName) => {"debug", "release"}
They are obtained from a MySQL database hence the dbContext object.
Here is a simple code that I was to translate in LINQ because 2 foreach are things of the past:
var choiceList = new List<List<string>>();
foreach (Platform platform in dbContext.Platforms.ToList())
{
foreach (Configuration configuration in dbContext.Configurations.ToList())
{
choiceList.Add(new List<string>() { platform.CodeName, configuration.CodeName });
}
}
This code gives my exactly what I want, keeping the platform name first which looks like :
var results = new List<List<string>>() {
{"test", "debug"},
{"test", "release"},
{"PC", "debug"}
{"PC", "release"}
{"Nintendo", "debug"}
{"Nintendo", "release"}};
But if I translate that to this, my list contains item in a different order:
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
I will end up with this, where the platform name isn't always first, which is not what is desired:
var results = new List<List<string>>() {
{"debug", "test"},
{"release", "test"},
{"debug", "PC"}
{"PC", "release"}
{"debug", "Nintendo"}
{"Nintendo", "release"}};
My question is, is it possible to obtain the desired result using LINQ?
Let me know if I'm not clear or my question lacks certain details.
Thanks
EDIT: So Ivan found the explanation and I modified my code in consequence.
In fact, only the Enumerable in front of the SelectMany needed the .ToList().
I should also have mentioned that I was stuck with the need of a List>.
Thanks everyone for the fast input, this was really appreciated.
When you use
var choiceList = dbContext.Platforms.SelectMany(p => dbContext.Configurations.Select(t => new List<string>() { p.CodeName, t.CodeName })).ToList();
it's really translated to some SQL query where the order of the returned records in not defined as soon as you don't use ORDER BY.
To get the same results as your nested loops, execute and materialize both queries, and then do SelectMany in memory:
var platforms = dbContext.Platforms.ToList();
var configurations = dbContext.Configurations.ToList();
var choiceList = platforms.SelectMany(p => configurations,
(p, c) => new List<string>() { p.CodeName, c.CodeName })
.ToList();
Rather than projecting it out to an array, project it out two a new object with two fields (potentially an anonymous object) and then, if you need it, project that into a two element array after you have retrieved the objects from the database, if you really do need these values in an array.
Try this-
var platforms= dbContext.Platforms.Select(x=>x.CodeName);
var configurations=dbContext.Configurations.Select(x=>x.CodeName);
var mix=platforms.SelectMany(num => configurations, (n, a) => new { n, a });
If you want to learn more in detail- Difference between Select and SelectMany
Using Linq to Entities I need to filter an my entity set based on a list of complex objects that represent criteria. For example:
var criteria = new[]
{
new { FirstName = "Bob", LastName = "Smith" },
new { FirstName = "Jane", LastName = "Doe" }
};
I am try to do the following:
var filtered = PersonEntities.where(person => criteria.Any(c => c.FirstName == person.FirstName && c.LastName == person.LastName)).ToList();
This results in an error due to the query builder not being able to process the complex object. I know that if I had an Id value to filter on, I could simply select it into an in-memory list and replace the complex object with it in the linq query criteria. However I have not been able to find a solution when multiple fields are required in the criteria. Any ideas?
As suggested in the comment, in this specific case we can work around the problem easily without having to build a complex Expression tree manually. If your criteria should be what you declared, you can always re-shape it to a list of strings containing firstname and lastname separated by a underscore (this should not be any of the valid characters for name). Then you have just primitive collection/array and can totally use it in the linq query, something like this:
var first_last = criteria.Select(e=> string.Format("{0}_{1}",e.FirstName,e.LastName));
var filtered = PersonEntities.Where(person => first_last.Any(e=>e == person.FirstName + "_" + person.LastName)).ToList();
If building the Expression tree, I guess you have to even touch the so-called ExpressionVisitor, it's quite a long code (at least 7-8 times of what we've done here).
Currently I can query my DataTable (dtContacts) like this:
If I am looking for a person whose FirstName is "John" and LastName is "Smith". My result will be
DataTable dt = dtContacts.AsEnumerable().Where(c => c.Field<string>("FirstName") == "John" && c.Field<string>("LastName") == "Smith").CopyToDataTable();
But such static querying does not help me because my conditions may be slightly more complicated than this. So I would like my LINQ to be a bit more dynamic so it can accommodate the following scenario.
I am going to save my condition in a Dictionary<string,string> like this:
Dictionary<string, string> dicConditions = new Dictionary<string, string>();
dicConditions.Add("FirstName", "John");
dicConditions.Add("LastName", "Smith");
dicConditions.Add("IsCustomer", "Yes");
Note that we can add as many conditions as we want to this dictionary.
And let's assume that the main DataTable dtContacts always contains the column names that are mentioned in the dictionary keys so we do not need to check for that every time.
In the new version we would like our LINQ to act according to dicConditions and return all the values whose FirstName is "John", LastName is "Smith" and IsCustomer is "Yes".
How should this new dynamic LINQ query look like?
You could do it like this:
DataTable dt = dtContacts
.AsEnumerable()
.Where(c =>
dicConditions.All(kv => c.Field<string>(kv.Key) == kv.Value)
).CopyToDataTable();
The idea is to apply LINQ twice - once to the dtContacts, and once to dicConditions inside the Where clause.
You can build the predicate you showed above from a dictionary using an approach like this:
using StringKvp = System.Collections.Generic.KeyValuePair<string, string>;
using DataRowPredicate = System.Func<System.Data.DataRow, bool>;
...
var pred = dicConditions.Aggregate<StringKvp, DataRowPredicate>(r => true,
(net, curr) => r => net(r) && r.Field<string>(curr.Key) == curr.Value);
Then it is just a matter of filtering your collection with this predicate:
var dt = dtContacts.AsEnumerable().Where(pred).CopyToDataTable();
something like this...
var result= dtContacts.Rows.OfType<DataRow>().Where(r=>dicConditions.All(d=>r[d.key]==d.Value));
You may need to properly convert the values of the dictionary to be compatible with the datatable...
Use Regex to support wildcards...etc...