query list with linq lambda expressions - c#

How would I get participants that are in a list of counties? I get the counties in var counties, then I want to get all of the participants that have a CountyOfParticipationId that is in the list of counties.
if (collaborationId != null)
{
var counties = (from c in db.CountyCollaborations
where c.CollaborationId == collaborationId
select c).ToList();
participants = participants.Where(p => p.CountyOfParticipationId in counties);
}

.Where(p => counties.Contains(p.CountyOfParticipationId))
Now if there's a lot of data be careful with the complexity of this. Contains in a list is O(n), so overall the algorithm is O(n*m) with n,m being the # of participants and the # of counties.
For better performance you could store the counties in a HashSet, which has O(1) Contains, meaning O(n) overall.
Of course if the number of counties is small it doesn't matter, really.
EDIT: Just noted that your list doesn't contain the ids but full objects. For the code above to work you also need to change your linq query from select c to select c.Id or something like that (don't know the name of the field).

participants = participants
.Where(p => counties.Any(c=> c.CountyId == p.CountyOfParticipationId) )
Or
participants.Where(p => p.County.CollaborationId == collaborationId)
should also work if you have set up relations properly

This might be better in some situations since you won't have to store counties separately if the linq method is translating the expression to sql behind the scences.
participants = (from p in participants
join c in
db.CountyCollaborations
.Where(cty=>cty.CollaborationId == collaborationId)
on p.CountyOfParticipationId equals c.CountyId
select p);

Assuming each county has a CountyId:
participants = participants.Where( p =>
counties.Select(c=> c.CountyId ).Contains( p.CountyOfParticipationId) );

Related

Filter items from database based on a List<>

I have a method that accepts two List<int> for which I need to get data from the database based on the List<>s.
So, I receive a List<PersonId> and List<NationalityId> for example, and I need to get a result set where records match the PersonIds and NationalistIds.
public List<PersonDTO> SearchPeople(List<int> persons, Lisy<int> nationalities)
{
var results = (from c in myDbContect.People where .... select c).ToList();
}
Note that I think Lists might be null.
Is there an efficient way?
I was going to try:
where ((persons != null && persons.Count > 0) && persons persons.Contains(x=>x.PersonId))
But this would generate rather inefficient SQL, and as I add more search parameters, the linq may get very messy.
Is there an efficient way to achieve this?
The join method may be easy to read, but the issue I face is that IF the input list is empty, then it shouldn't filter. That is, if nationalities is empty, don't filter any out:
var results = (from c in entities.Persons
join p in persons on c.PersonId equals b
join n in nationalities on c.NationalityId equals n
equals n
select c).ToList();
This would return no results if any of the lists were empty. Which, is bad.
If you join an IQueryable with an IEnumerable (in this case, entities.Persons and persons), your filtering will not happen within your query. Instead, your IQueryable is enumerated, retrieving all of your records from the database, while the join is performed in memory using the IEnumerable join method.
To perform your filtering against a list within your query, there are two main options:
Join using an IQueryable on both sides. This might be possible if your list of ids comes from the execution of another query, in which case you can use the underlying query in your join instead of the resulting set of ids.
Use the contains operator against your list. This is only possible with small lists, because each additional id requires its own query parameter. If you have many ids, you can possibly extend this approach with batching.
If you want to skip filtering when the list is empty, then you might consider using the extension method invocation instead of the LINQ syntax. This allows you to use an if statement:
IQueryable<Person> persons = entities.persons;
List<int> personIds = new List<int>();
if(personIds.Count > 0)
{
persons = persons.Where(p => personIds.Contains(p.PersonId));
}
var results = persons.ToList();
Note that the Where predicate uses option #2 above, and is only applied if there are any ids in the collection.
If you want to get all the records for persons for example if the list is empty and then filter by nationalityId list if its not empty you can do something like this:
List<int> personsIds = ...;
List<int> nationalitiesIds = ...;
var results = (from c in entities.Persons
join p in persons on c.PersonId equals b
join n in nationalities on c.NationalityId equals n
where ((personsIds == null || personsIds.Contains(p.Id))
&& (nationalitiesIds == null || nationalitiesIds.Contains(n.Id))
select c).ToList();

Using LINQ to select desired results between two related IEnumerable query objects

I think this is kind of a basic question but I'm getting confused. I have two objects, Orders and OrderTags. In the database, Orders has no relation to OrderTags, but OrderTags has a FK relation to Orders.
So I capture both objects in my context like so:
orders = context.Orders;
tags = context.OrderTags.Where(tag=> tag.ID = myID);
Now I want to reduce the orders list to only be equal to the orders that exist in my tags list. Here is my best pseudocode of what I want to do:
orders = orders.Where(every order id exists somewhere in the tags list of order ids)
For clarification, each Tag object has a TagID and an OrderID. So I only want the orders that correspond to the tags I have looked up. Can anyone assist me with the syntax so I can get what I'm looking for?
Using a LINQ query:
var results = (from o in context.Orders
join t in context.Tags on o.OrderId equals t.OrderId
where t.ID == myID
select o ).ToList();
Using LINQ query:
orders = orders.Where(order => tags.Contains(tag => tag.ID == order.OrderID)).ToList();
Using a LINQ query with lambda expressions:
orders.RemoveAll(x => !tags.ConvertAll(y => y.tagId).Contains(x.tagID));
Something like this should work.
orders = orders.Where(o=>tags.Contains(t=>o.ID == t.OrderID));
You could also just perform a join.

Order by in linq on joining

I have to show the movies which have most wanted to see status. For this I Join two tables 1. MovieInformation and 2. SeentItWantToSeeIt. I calculated most wanted to see movie with this code snippets.
public class RangkedMovieList
{
public int MovieID { get; set; }
public int count { get; set; }
}
rankedList = db.SeenItWantToSeeIt.Where(m => m.Status == 1 && m.MovieID != null)
.GroupBy(m => m.MovieID)
.Select(g => new RangkedMovieList
{ MovieID = g.Key.Value,
count = g.Count()
})
.ToList();
AllMovieInfo is Iqueryable with which I joined wanted to see movies.
AllMovieInfo = (from r in rankedList
join a in AllMovieInfo on r.MovieID equals a.MovieID
orderby r.count descending
select a).Distinct().AsQueryable();
This query works fine and orders the result by MovieID but I want to order all movies by their wanted to see count. Though I have written code to orderby r.count descending, Its not working. Please help me out.
I'm guessing your second LINQ query is operating at the database level. If so, it's likely that the Distinct call won't honor the previously specified sort order (in fact, if you drop the Distinct call and debug your output, it should appear sorted correctly). Typically for simple queries you can fix this by swapping the order of the sort (i.e., orderby) and Distinct call while keeping the query on the database level, as answered in another post, but your case is a little more complicated since you need to work with rankingList.
In your case you could bring it down to the LINQ to Objects level by adding a ToList() as you did for the first query, or an AsEnumerable(). At that level you wouldn't need to worry about swapping the order of the sort or distinct call. However, you'll likely need to use the overloaded Distinct method that accepts an IEqualityComparer and provide a comparer for AllMovieInfo. Check out the link for an example of how to implement that.
var query = (from r in rankedList
join a in AllMovieInfo on r.MovieID equals a.MovieID
select new { Info = a, Rank = r.count })
.AsEnumerable()
.OrderByDescending(o => o.Rank);
.Select(o => o.Info)
.Distinct(); // will likely need an IEqualityComparer here
// this could be combined with the above query, but I split it for clarity
AllMovieInfo = query.AsQueryable();
You might not need the AsQueryable at that point since it's already on the client at that point, in memory.

Help with LINQ query

I currently a list of a Supplier class, within that supplier class is a list of orders.
Each order has a userID and an empty string variable for username.
I then have a list of users which contains userID and username.
The way I am doing this now is:
foreach(supplier s in SupplierList)
{
foreach (order o in s.childorders)
{
user u = _users.First(p => p.userid == o.userid);
o.username = u.username;
}
}
I feel this might be a little inefficient and I was wondering if it is possible to compact it down into one linq query?
The logic should be
set supplierslist.childorders.username to the value in _users where supplierslist.childorders.userid == _users.userid.
Im fairly new to Linq so any advice for this would be apreciated, or also if its a bad idea and to leave it as it is / reasons why would be good too.
Thanks
What you want to do here is iterate over a collection (many collections, really, but it doesn't make a difference) and mutate its members. LINQ is not really targeted at performing mutating operations but rather at querying. You can do it with LINQ, but it's against the spirit of the tool.
If you are constructing the SupplierList yourself, it might be possible to fetch the data appropriately with LINQ so that it comes pre-populated as you want it to be.
Otherwise, I 'd leave the foreach as it is. You can make a dictionary that maps ids to users to make the inner loop faster, but that's your call and it depends on your data size.
var orderUserPairs = SupplierList
.SelectMany(s => s.ChildOrders)
.Join(_users, o => o.UserId, u => u.userId, (Order, User) => new {Order, User});
foreach (var orderUserPair in orderUserPairs)
orderUserPair.Order.username = orderUserPair.User.username;
Though having both username and userId as part of order looks suspicious.
First a question...
It looks like you are operating on every order. Why do you need to cycle through the supplierlist first since you don't seem to be using it inside the loop? Unless there are orders that don't belong to any supplierlist, you might be able to skip that step.
If that isn't the case, then I think you can use a join. If you aren't familiar with the syntax for joins in linq, this is one (simplified) way to approach it:
var x = from S in SupplierList
join C in childorders on C.supplierlistID equals S.ID
where [whatever you need here if anything]
select new { field1, field2};
foreach var y in x
{
}
Note I assumed a foreign key in childorders to supplierlist. If that isn't the case you will have to modify accordingly.
Hope that helps.
You need to use SelectMany or join depending on weather you are using linq-to-sql or linq with local collections. If you are using local collections the better way is to use join, else use SelectMany.
Like this...join:
var selection = (from s in SupplierList
join o in s.childholders on s.userid equals o.userid
select new { username = o.username);
or, in case of linq-to-sql:
var selection = (from s in SupplierList
from o in s.childholders
select { username = o.username);
You can then use the anonymous type you projected the way you want.
I agree with Jon, but you could say:
var orders = (from s in supplier
from o in s.childorders
select new
{
Order = o,
User = _users.First(p => p.userid == o.userid)
}).ToList();
foreach(var order in orders) {
order.Order.username = order.User.username;
}
Untested of course :)
If users list contains many elements, it can be really slow so I'd use a temporary dictionary:
var userById = users.GroupBy(x => x.userid)
.ToDictionary(x => x.Key, x => x.First());
foreach(var order in supplier.SelectMany(x => x.childorders))
{
order.username = userById[order.userid].username;
}

Is there an efficient way in LINQ to use a contains match if and only if there is no exact match?

I have an application where I am taking a large number of 'product names' input by a user and retrieving some information about each product. The problem is, the user may input a partial name or even a wrong name, so I want to return the closest matches for further selection.
Essentially if product name A exactly matches a record, return that, otherwise return any contains matches. Otherwise return null.
I have done this with three separate statements, and I was wondering if there was a more efficient way to do this. I am using LINQ to EF, but I materialize the products to a list first for performance reasons.
productNames is a List of product names (input by the user).
products is a List of product 'records'
var directMatches = (from s in productNames
join p in products on s.ToLower() equals p.name.ToLower() into result
from r in result.DefaultIfEmpty()
select new {Key = s, Product = r});
var containsMatches = (from d in directMatches
from p in products
where d.Product == null
&& p.name.ToLower().Contains(d.Key)
select new { d.Key, Product = p });
var matches = from d in directMatches
join c in containsMatches on d.Key equals c.Key into result
from r in result.DefaultIfEmpty()
select new {d.Key, Product = d.Product ?? (r != null ? r.Product: null) };
If you have a small to medium-sized list in-memory, take a look at LiquidMetal and for phonetic matches, the Soundex algorithm to rank the closest matches.
If you are using SQL Server, look into Full-Text Search, which is what Stack Overflow uses. Otherwise, here is how I implemented a keyword-based search.

Categories

Resources