Delegates in Linq - c#

I have a list of entities that contains search terms, clicks etc.
The user should be able to compare the amount of clicks between the entities - i.e. all the entities that contains the word "free sample" in the beginning of the search and compare with all the entities that contains the phrase "arrest me" in the end of the phrase.
I can do this with multiple foreach and switch (because the user choose the query and the part of the string) but i understand that by using Linq i can use the "start with" and "end with" functions. could you please guide me through?

It sounds like you want a list of things that start with "free sample" and then another that ends with "arrest me". Assuming that's what you want, you'll do something like this.
// Assumes myStuff is where all of your data is right now
var startsWith = myStuff.Where(x => x.MyString.StartsWith("free sample"));
var endsWith = myStuff.Where(x => x.MyString.EndsWith("arrest me"));
// Now you can do whatever comparisons between the two lists you need
Of course, if you just want all of them that start with "free sample" and end with "arrest me" you can just do this
var hasBoth = myStuff.Where(x => x.MyString.StartsWith("free sample") && x.MyString.EndsWith("arrest me"))

LINQ Where() to return a set of entities which satisfies a condition:
var filtered = entities.Where(e =>
e.SearchPattern.StartsWith("Start")
&&
e.SearchPattern.EndsWith("End"));
LINQ Single()/SingleorDefault() to return a single entity which satisfies a condition, if more than one found - exception will be thrown.
var filtered = entities.Single(e =>
e.SearchPattern.StartsWith("Start")
&&
e.SearchPattern.EndsWith("End"));
LINQ First()/Last() to return first/last entity for the given condition...
var filtered = entities.First(e =>
e.SearchPattern.StartsWith("Start")
&&
e.SearchPattern.EndsWith("End"));
See Enumerable class methods for the full list

The functions you mention are just string functions. s.StartsWith("abc") and s.EndsWith("xyz"). In a LINQ query you can use them in a where clause.
var startIndexes = phrases
.Where(p => p.StartsWith("free sample")
.Select((p, i) => i);
var endIndexes = phrases
.Where(p => p.EndsWith("arrest me")
.Select((p, i) => i);
These two queries return the indexes of phrases that start, respectively end with a certain string. I use an overload of select that accepts the index as second parameter.

Related

Searching any word using Linq

In my winform app I have a form where user can search for product by typing any text in search field. For example the product description might be stored as "Adidas aftershave 100Ml" . But user can type any thing like 100 Ml Aftershave. So I would like to query using linq to get all records where description contains any of these word(100 Ml Aftershave).
So far I have done something like this:
List<string>txtList = txtSearchTerm.Text.Split(' ').ToList();
return dbContext.productRepo.Where(t => txtList.Any(b =>
t.ProductDescription.StartsWith(b)
|| t.ProductDescription.EndsWith(b)
|| t.ProductDescription.Contains(b)
)).Select(x => x.ProductDescription).ToList();
Any other way of achieving this in better way ,making it more quick or any other improvement.
Thanks
Well, you are testing if the description starts, ends and contains something. The first two are already redundant because Contains "contains" them:
var matchingDescriptions = dbContext.productRepo
.Where(x => txtList.Any(x.ProductDescription.Contains))
.Select(x => x.ProductDescription));
Another easy optimization(for Linq-To-Objects), I would order the words by length first:
var txtList = txtSearchTerm.Text.Split(' ').OrderBy(s => s.Length).ToList()
One thing you can straightaway improve is to remove StartsWith and EndsWith , Becuase you are already doing t.ProductDescription.Contains(b) .
List<string>txtList = txtSearchTerm.Text.Split(' ').ToList();
return dbContext.productRepo.Where(t => txtList.Any(b =>
t.ProductDescription.Contains(b)
)).Select(x => x.ProductDescription).ToList();

Linq return all records from entity where a field contains one or more words from a list

Given a List<string> How to return all records in an entity that has a field containing one or more words from the list.
I tried the below which does not work and I'm starting to go around in circles a bit:
List<string> searchwords = new List<string>() {"word1","word2"};
var results = context.activities
.Where(a => a.Title.Contains(searchwords.Any().ToString())).ToList();
The problem with your current code:
var results = context.activities.Where(a =>
a.Title.Contains(searchwords.Any().ToString())).ToList();
is that you have your needle and haystack backwards. The fact that you needed to call .ToString() on Any() should have tipped you off. Any() returns a bool, which you're casting to a string, so you're just checking whether Title contains the string "True". Definitely not what you want. You want something closer to:
var results = context.activities
.Where(a => searchwords.Any(searchWord => a.Title.Contains(searchWord)));

ASP.NET MVC C# Select and Where Statements

I'm having trouble understanding .Select and .Where statements. What I want to do is select a specific column with "where" criteria based on another column.
For example, what I have is this:
var engineers = db.engineers;
var managers = db.ManagersToEngineers;
List<ManagerToEngineer> matchedManager = null;
Engineer matchedEngineer = null;
if (this.User.Identity.IsAuthenticated)
{
var userEmail = this.User.Identity.Name;
matchedEngineer = engineers.Where(x => x.email == userEmail).FirstOrDefault();
matchedManager = managers.Select(x => x.ManagerId).Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
}
if (matchedEngineer != null)
{
ViewBag.EngineerId = new SelectList(new List<Engineer> { matchedEngineer }, "PersonId", "FullName");
ViewBag.ManagerId = new SelectList(matchedManager, "PersonId", "FullName");
}
What I'm trying to do above is select from a table that matches Managers to Engineers and select a list of managers based on the engineer's id. This isn't working and when I go like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
I don't get any errors but I'm not selecting the right column. In fact the moment I'm not sure what I'm selecting. Plus I get the error:
Non-static method requires a target.
if you want to to select the manager, then you need to use FirstOrDefault() as you used one line above, but if it is expected to have multiple managers returned, then you will need List<Manager>, try like:
Update:
so matchedManager is already List<T>, in the case it should be like:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).ToList();
when you put Select(x=>x.ManagerId) after the Where() now it will return Collection of int not Collection of that type, and as Where() is self descriptive, it filters the collection as in sql, and Select() projects the collection on the column you specify:
List<int> managerIds = managers.Where(x => x.EngineerId == matchedEngineer.PersonId)
.Select(x=>x.ManagerId).ToList();
The easiest way to remember what the methods do is to remember that this is being translated to SQL.
A .Where() method will filter the rows returned.
A .Select() method will filter the columns returned.
However, there are a few ways to do that with the way you should have your objects set up.
First, you could get the Engineer, and access its Managers:
var engineer = context.Engineers.Find(engineerId);
return engineer.Managers;
However, that will first pull the Engineer out of the database, and then go back for all of the Managers. The other way would be to go directly through the Managers.
return context.Managers.Where(manager => manager.EngineerId == engineerId).ToList();
Although, by the look of the code in your question, you may have a cross-reference table (many to many relationship) between Managers and Engineers. In that case, my second example probably wouldn't work. In that case, I would use the first example.
You want to filter data by matching person Id and then selecting manager Id, you need to do following:
matchedManager = managers.Where(x => x.EngineerId == matchedEngineer.PersonId).Select(x => x.ManagerId).ToList();
In your case, you are selecting the ManagerId first and so you have list of ints, instead of managers from which you can filter data
Update:
You also need to check matchedEngineer is not null before retrieving the associated manager. This might be cause of your error
You use "Select" lambda expression to get the field you want, you use "where" to filter results

Using Linq Grouping how to I order by specific Group then remaining Groups descending

This Code is working example of the content of a select case statement. It is in response to a link button click who's ID is passed in via Session variable. The link buttons represent High(3) Medium(2) and Low(1) Risk categories.
The logic here is; if you select Medium(2) it's related rows (riskCategory =2) are displayed first then the remaining rows ( risk category ) are listed descending so 3 then 2 then 0)
As I said my ugly implementation of the Linq Concat function to accomplish my goal does produce correct results, but it also showcases my need to spend more weekends reviewing and creating better intricate samples than the simple 101 Link Samples tutorial project provides.
There must be a more elegant way to group and order by while allowing for the groups to be ordered representing the selected group first, with remaining groups descending. Again Select Group 1 LowRiskCategory, I'll have to display LowRiskCategory first (1) then 3, 2, & 0 respectively in the sorted results set.
var midQuery = enumerableVersionTable.Where(x => x["RiskCategory"].Equals(intRiskCategoryEnum));
midQuery.OrderByDescending(v => v["DateOfService"]);
midQuery.OrderBy(v => v["Reviewed"]);
var midQueryZero = enumerableVersionTable.Where(x => x["RiskCategory"].Equals(0));
midQueryZero.OrderByDescending(v => v["DateOfService"]);
midQueryZero.OrderBy(v => v["Reviewed"]);
var midQueryOne = enumerableVersionTable.Where(x => x["RiskCategory"].Equals(1));
midQueryOne.OrderByDescending(v => v["DateOfService"]);
midQueryOne.OrderBy(v => v["Reviewed"]);
var midQueryThree = enumerableVersionTable.Where(x => x["RiskCategory"].Equals(3));
midQueryThree.OrderByDescending(v => v["DateOfService"]);
midQueryThree.OrderBy(v => v["Reviewed"]);
var querySummation = midQuery.Concat(midQueryThree);
querySummation = querySummation.Concat(midQueryOne);
querySummation = querySummation.Concat(midQueryZero);
dtQueryResults = querySummation.CopyToDataTable()
Just the sight of those hardcoded numeral values after the translated enum value for case 2:
makes me wana hurl. Theres gotta be more elegant way to do the groups. Order by a specific group. and of course apply all my other odd sorting, as you see date of service and reviewed.
Lastly if you going to AGAIN vote down
at least explain why please thank you
var dtQueryResults = yourData
.OrderByDescending(v => v["RiskCategory"] == intRiskCategoryEnum)//true for ==2 goes first, false goes then
.ThenBy(v => v["RiskCategory"]) //the rest is sorted normally
.ThenBy(v => v["Reviewed"]) //inside the groups, the rest of your sorts is used
.ThenByDescending(v => v["DateOfService"]);
Just change the lambda which you have used for the OrderBy. you are not limited to picking up just one field. I'd use a tertiary expression to select what to sort on based on what is selected.

Identify items in one list not in another of a different type

I need to identify items from one list that are not present in another list. The two lists are of different entities (ToDo and WorkshopItem). I consider a workshop item to be in the todo list if the Name is matched in any of the todo list items.
The following does what I'm after but find it awkward and hard to understand each time I revisit it. I use NHibernate QueryOver syntax to get the two lists and then a LINQ statement to filter down to just the Workshop items that meet the requirement (DateDue is in the next two weeks and the Name is not present in the list of ToDo items.
var allTodos = Session.QueryOver<ToDo>().List();
var twoWeeksTime = DateTime.Now.AddDays(14);
var workshopItemsDueSoon = Session.QueryOver<WorkshopItem>()
.Where(w => w.DateDue <= twoWeeksTime).List();
var matches = from wsi in workshopItemsDueSoon
where !(from todo in allTodos
select todo.TaskName)
.Contains(wsi.Name)
select wsi;
Ideally I'd like to have just one NHibernate query that returns a list of WorkshopItems that match my requirement.
I think I've managed to put together a Linq version of the answer put forward by #CSL and will mark that as the accepted answer as it put me in the direction of the following.
var twoWeeksTime = DateTime.Now.AddDays(14);
var subquery = NHibernate.Criterion.QueryOver.Of<ToDo>().Select(t => t.TaskName);
var matchingItems = Session.QueryOver<WorkshopItem>()
.Where(w => w.DateDue <= twoWeeksTime &&
w.IsWorkshopItemInProgress == true)
.WithSubquery.WhereProperty(x => x.Name).NotIn(subquery)
.Future<WorkshopItem>();
It returns the results I'm expecting and doesn't rely on magic strings. I'm hesitant because I don't fully understand the WithSubquery (and whether inlining it would be a good thing). It seems to equate to
WHERE WorkshopItem.Name IS NOT IN (subquery)
Also I don't understand the Future instead of List. If anyone would shed some light on those that would help.
I am not 100% sure how to achieve what you need using LINQ so to give you an option I am just putting up an alternative solution using nHibernate Criteria (this will execute in one database hit):
// Create a query
ICriteria query = Session.CreateCriteria<WorkShopItem>("wsi");
// Restrict to items due within the next 14 days
query.Add(Restrictions.Le("DateDue", DateTime.Now.AddDays(14));
// Return all TaskNames from Todo's
DetachedCriteria allTodos = DetachedCriteria.For(typeof(Todo)).SetProjection(Projections.Property("TaskName"));
// Filter Work Shop Items for any that do not have a To-do item
query.Add(SubQueries.PropertyNotIn("Name", allTodos);
// Return results
var matchingItems = query.Future<WorkShopItem>().ToList()
I'd recommend
var workshopItemsDueSoon = Session.QueryOver<WorkshopItem>()
.Where(w => w.DateDue <= twoWeeksTime)
var allTodos = Session.QueryOver<ToDo>();
Instead of
var allTodos = Session.QueryOver<ToDo>().List();
var workshopItemsDueSoon = Session.QueryOver<WorkshopItem>()
.Where(w => w.DateDue <= twoWeeksTime).List();
So that the collection isn't iterated until you need it to be.
I've found that it's helpfull to use linq extension methods to make subqueries more readable and less awkward.
For example:
var matches = from wsi in workshopItemsDueSoon
where !allTodos.Select(it=>it.TaskName).Contains(wsi.Name)
select wsi
Personally, since the query is fairly simple, I'd prefer to do it like so:
var matches = workshopItemsDueSoon.Where(wsi => !allTodos.Select(it => it.TaskName).Contains(wsi.Name))
The latter seems less verbose to me.

Categories