best way to use LINQ to query against a list - c#

i have a collection
IEnumerable<Project>
and i want to do a filter based on project's Id property to included any id that is in a list:
List<int> Ids
what is the best way to do a where clause to check if a property is contained in a list.

var filteredProjectCollection = projectCollection.Where(p => Ids.Contains(p.id));

You may be able to get a more efficient implementation using the Except method:
var specialProjects = Ids.Select(id => new Project(id));
var filtered = projects.Except(specialProjects, comparer);
The tricky thing is that Except works with two collections of the same type - so you want to have two collections of projects. You can get that by creating new "dummy" projects and using comparer that compares projects just based on the ID.
Alternatively, you could use Except just on collections of IDs, but then you may need to lookup projects by the ID, which makes this approach less appealing.

var nonExcludedProjects = from p in allprojects where Ids.Contains(p => p.Id) select p;

If you're going to use one of the .Where(p=> list.Contains(p)) answers, you should consier first making a HashSet out of the list so that it doesn't have to do an O(n) search each time. This cuts running time from O(mn) to O(m+n).

I'm not sure that I understand your question but I'll have a shot.
If you have: IEnumerable enumerable,
and you want to filter it such that it only contians items that are also present in the list: List list,
then: IEnumerable final = enumerable.Where(e => list.Contains(e));

Related

Linq select objects by comparing from two lists of type int

I have a List<Organisation> Organisations.
Each Organisation has a property List<int> Categories.
I also have a separate List<int> DisplayCategories
I want to create a new List<Organisation> called DisplayOrganisations.
DisplayOrganisations will contain Organisation's whos Categories appear in my List<int> DisplayCategories.
How can I achieve this using linq or lambda?
The following is incorrect:
DisplayOrganisations = (from o in Organisations
where o.Categories.Intersect(DisplayCategories.CategoryIds)
select o).ToList();
I get an error:
Cannot implicitly convert type
'System.Collections.Generic.IEnumerable' to 'bool'
I believe that this is because I am selecting Organisations who's sub list of Categories need to be compared to a separate list DisplayCategories.
Any ideas?
Sure intersect will not give a boolean. you may want to check if there is any elements exist in it use Any.
DisplayOrganisations = Organisations
.Where(o => o.Categories.Intersect(DisplayCategories.CategoryIds)
.Any()).ToList();
Or better approach.
DisplayOrganisations = Organisations.Where(o => o.Categories
.Any(xDisplayCategories.CategoryIds.Contains)).ToList();
As you can see in the image Intersect uses hashset which is faster than second approach because Hashset has time complexity of O(1) for both adding and removing items. So in general Intersect is O(n+m). but the second approach using Any and Contains had time complexity of O(n^2) which could become much slower.Thanks to Dmitry Dovgopoly.
If you need only those Organisations where all elements are in CategoryIds list you can use mixture of Except() and Any() methods :
var DisplayOrganisations = Organisations.Where(o =>
!o.Categories.Except(DisplayCategories.CategoryIds).Any()).ToList();
If you just want to check that there is at least one id in the list of ids you can use .Any method :
var DisplayOrganisations = Organisations.Where(o => o.Categories
.Any(DisplayCategories.CategoryIds.Contains)).ToList();

Selecting items in an ordered list after a certain entry

I have an ordered list of objects. I can easily find an item in the list by using the following code:
purchaseOrders.FirstOrDefault(x => x.OurRef.Equals(lastPurchaseOrder, StringComparison.OrdinalIgnoreCase))
What I want to do is select all the items in the list that appear after this entry. How best to achieve this? Would it to be to get the index of this item and select a range?
It sounds like you want SkipWhile:
var orders = purchaseOrders.SkipWhile(x => !x.OurRef.Equals(...));
Once the iterator has stopped skipping, it doesn't evaluate the predicate for later entries.
Note that that code will include the entry that doesn't match the predicate, i.e. the one with the given reference. It will basically give you all entries from that order onwards. You can always use .Skip(1) if you want to skip that:
// Skip the exact match
var orders = purchaseOrders.SkipWhile(x => !x.OurRef.Equals(...)).Skip(1);
This will be linear, mind you... if the list is ordered by x.OurRef you could find the index with a binary search and take the range from there onwards... but I wouldn't do that unless you find that the simpler code causes you problems.
Probably you should take a look at LINQ's combination of Reverse and TakeWhile methods, if I understand your question correctly.
It may look like purchaseOrder.Reverse().TakeWhile(x => !x.OurRef.Equals(lastPurchaseOrder, StringComparison.OrdinalIgnoreCase)).
Sorry if code is unformatted, I'm from mobile web right now.
May be you want something like this:
int itemIndex = list.IndexOf(list.FirstOrDefault(x => x.OurRef.Equals(lastPurchaseOrder, StringComparison.OrdinalIgnoreCase));
var newList = list.Where((f, i) => i >= itemIndex);

How to use Orderby Clause with IEnumerable

I have written following code:
IEnumerable<Models.bookings> search = new List<bookings>();
search = new available_slotsRepositories().GetAvailableSlot(param1,param2);
var data = from s in search.AsEnumerable().
OrderByDescending(c => c.BookingDate)
select s;
i have also tried this and it does not work:
search.OrderByDescending(c => c.BookingDate);
Third line gives me following error:
Expression cannot contain lambda expressions
Any one guide me how can i fix this issue?
Any help would be appreciated.
Thank you!
why r u using new List()??
follow the below pattern
IEnumerable<Step> steps = allsteps.Where(step => step.X <= Y);
steps = steps.OrderBy(step => step.X);
NOTE:
IEnumerable makes no guarantees about ordering, but the implementations that use IEnumerable may or may not guarantee ordering.
For instance, if you enumerate List, order is guaranteed, but if you enumerate HashSet no such guarantee is provided, yet both will be enumerated using the IEnumerable interface
Perhaps you are looking for the IOrderedEnumerable interface? It is returned by extensions methods like OrderBy() and allow for subsequent sorting with ThenBy().
Have you tried
var data = (from s in search
OrderByDescending(c => c.BookingDate)
select s).ToList();
That will make a List which is IEnumerable.
I'm not sure why you need "new" if as you say GetAvailableSlot returns an IEnumerable. What I think your code should look like assuming GetAvailableSlot returns IEnumerable is this:
var data = available_slotsRepositories().GetAvailableSlot(param1,param2).ToList().OrderByDescending(c => c.BookingDate);
All you're doing to your recordset is ordering the results there is no need to have multiple variables declared. If this still doesn't work then we need to see more of the code in order to see what the problem is...

C# collections and grabbing property value of each into a new array

I have a list of objects each with its own id property. I need to create an icollection of ids from the stored objects. Is there an elegant way other than looping through the list of objects, grabbing the id value and dumping it into the collection?
LINQ is your friend :)
List<int> ids = entities.Select(x => x.Id).ToList();
or into an array if you must (as per question title):
int[] ids = entities.Select(x => x.Id).ToArray();
Of course that does just iterate over the collections, but it's code you don't have to write yourself...
Basically, whenever you find yourself wanting to do "something like this" - transforming, aggregating, filtering, flattening etc a collection - you should look at whether LINQ can help you.
how about using Linq (which will do the iteration for you) using a Select() projection with the properties you want (in your case just the Id):
var idCollection = someList.Select(x=> x.Id).ToList();
Linq to the rescue:
var names=Controls.Select(w=>w.Name).ToArray(); // where this is a form

Which is better? DataTable.Select() or Linq for filtering objects?

I'm storing a collection of objects of custom class type. I've given the type below.
public class AnonymousClient
{
private string ipAddress, city, country, category;
private Guid id;
}
I may have to get the objects filtered based on city, country, category etc. I'm able to think of two ways -
Storing it in a dictionary
Dictionary<Guid, AnonymousClient>
and using Linq to filter the
objects.
Storing it in a DataTable with
multiple columns for the members
and using DataTable.Select() to
filter the records.
I guess both of them loop internally. Which one is faster and elegant? Any insights?
Using a DataTable would add quite a bit of overhead. It's easier to run a query once you set it up, because the code for it is already created, but it won't run as fast.
If you want to look up items using the id you can use a Dictionary<Guid, AnonymousClient>, if you only want to filter the data on the other fields you can just use a List<AnonymousClient>.
The most efficient would be to simply loop the collection yourself and pick out the items. LINQ is almost as efficient, but it adds a slight bit of overhead. On the other hand, the LINQ code gets very simple.
Example using the LINQ extension method Where:
var companies = clients.Where(c => c.category == "Company");
I would go with the first approach. But do you really need to store them in Dictionary? You already have them in a collection right? you could use LINQ to filter on the collection itself.
var cityFiltered = from p in AnonymousClientList where p.city == 'London' select p;
etc.. and build your filtered collection.
======UPDATE======
Here is the updated query for filtering using city:
var filtered = (from c in clients where c.Value.city=="London" select c).ToDictionary(x => x.Key, x => x.Value);
If you see, the first part basically returns you a boolean value for your condition and not the dictionary object that you need. By using the ToDictionary method, you convert back to a Dictionary. So, now after execution of the LINQ, you will have all AnonymousClient with city set to London.

Categories