Distinct rows from db table with unique IDs but same names - c#

I am creating a videorental system and I'm told that there are to be multiple entries of the same movie, as in a real videostore, but they should all have unique IDs. I'm using LINQ to fetch the data, and currently my view shows me the entire table, so the same movie name gets repeated alot. I tried using the .Distinct extension method, but since the IDs are not the same, they are all distinct. I need to ignore the ID portion of the object, but can't figure it out.
public static List<Movie> GetAllMovies()
{
return Context.Movie.ToList();
}
public static List<Movie> GetAllMoviesDistinct()
{
return Context.Movie.Distinct().ToList();
}
These two methods do the exact same thing

You can do this with MoreLINQ:
var result = Context.Movie
.DistinctBy(m => new {
// take all properties except your ID
})
.ToList();

You can use GroupBy to get movies with for example unique name.
return Context.Movie.GroupBy(m => m.Name).Select(x => x.First()).ToList();

You'd need something like this:
public static List<string> GetAllMovieTitlesDistinct()
{
return Context.Movie.GroupBy(x => x.Title).Select(x => x.Key).ToList();
}
Since you have multiple entries for the same movie, it doesn't really make sense to return a random (or just the first) movie with a specific name. What I'd rather do, is get the unique movie titles, select one title and then list all the entries for that.
What the method above does is, it groups all the movies by their names and then just selects each unique movie title. Please note that it does not return a specific entry for each title, as that would be highly arbitrary and might lead to unwanted results.

var groupesList = Context.Movie.GroupBy(x => x.Name,
(key, val) => new {Key = key, Value = val}).ToList();
then you can call Key(unuque) or Value by key for all inform for example all ID

Related

remove nested values from object in linq

I have an object called copyAgencies which contains a class called Programs, which contains various information pertaining to a program (name, id, ect...).
I am trying to write a foreach loop to remove all programs that do not a match a certain id parameter.
For example, copyAgencies could contain 11 different programs; passing in 3 ids means that the other 8 programs should be removed from the copyAgencies object.
I tried the following code, which fails. Could you help me making it work?
foreach (int id in chkIds)
{
//copyAgencies.Select(x => x.Programs.Select(b => b.ProgramId == id));
copyAgencies.RemoveAll(x => x.Programs.Any(b => b.ProgramId != id)); //removes all agencies
}
If you only have one agency like you said in your comment, and that's all you care about, try this:
copyAgencies[0].Programs.RemoveAll(x => !chkIds.Contains(x.ProgramId));
An easy way to filter out values is to avoid removing the values you're not interesting but filtering the ones you're interested in:
var interestingPrograms = Programs.Where(p => chkIds.Contains(p.Id));
In order to apply this to your agencies you can simply enumerate agencies and filter out the Programs property
var chckIds = new List<int>() {1,2,3};
foreach (var a in agencies)
{
a.Programs = a.Programs.Where(p => chkIds.Contains(p.Id));
}

How to filter a list based on another list using Linq?

I have these two lists, one a list of Venue Objects, one a list of BlockedVenues objects.
I need to filter each item in the listOfAllVenues so that it doesn't contain any venue
that is blocked
IQueryable<Venue> listOfAllVenues = MyDB.Venues;
IQueryable<BlockedVenue> listOfBlockedVenues = Mydb.BlockedVenue;
//I need something to accomplish this please
// var listOfAllVenues_WithoutBlocked_Venues =
( Select All venues from listOfAllVenues
where listOfAllVenues.ID is NOT in
listOfBlockedVenues.VenueID)
Please note that yes both list types are different, but listOfAllVenues has an int ID field, and listOfBlockedVenues has a VenueID int field, i need to use these two
Many Thanks
Try this:
var filtered = listOfAllVenues
.Where(x=>!listOfBlockedVenues.Any(y=>y.VenueId == x.Id));
It will get all Venues where Id is not in blockedVenues list
As of NET 6, you can use ExceptBy.
var filtered = allVenues.ExceptBy(blockedVenues.Select(x => x.VenueID), venue => venue.ID);
This will get all venues except those whose ID is in blockedVenues.Select(x => x.VenueID)
https://learn.microsoft.com/en-us/dotnet/api/system.linq.enumerable.exceptby?view=net-6.0

Linq Select All Items Matching Array

I have a data collection of type IEnumerable<Objects.LabourHours> containing labour records for various employees. I wish to filter the list and return only records for selected employees, which is specified by a list of int[] employees containing the EmployeeIDs.
class LabourHours
{
public int ID {get;set;}
public int EmployeeID {get;set;}
public int HoursWorked {get;set;}
}
How would I go about this? I am sure this has been asked before but I can't find anything similar on here. The closest I have found involves grouping the records by UserID, which is not what I need - I need the actual records.
You can filter your list with LINQ Where using Contains method:
var result = list.Where(x => employees.Contains(x.EmployeeID));
If you want the result to preserve the order of the employees array, you can use Select on the array. From the doc, it "Projects each element of a sequence into a new form", which is basically what you'd want to do in this case.
var result = employees.Select(e => labourHours.First(l => l.EmployeeID == e));
Use FirstOrDefault if all employees don't necessarily have an associated labourHours entry.

Distinct elements in LINQ

I have a situation where i display a list of products for a customer. So, there are two kinds of products. So, if customer is registerd to two products, then both the products get displayed. So, I need to display distinct rows. I did this:
var queryProducts = DbContext.CustomerProducts.Where(p => p.Customers_Id ==
customerID).ToList().Select(r => new
{
r.Id,
r.Products_Id,
ProductName = r.Product.Name,
ShortName = r.Product.ShortName,
Description = r.Product.Description,
IsActive = r.Product.IsActive
}).Distinct();
In this, customerID is the value that i get from dropdownlist. However, it still displays the same row twice. So, can you please let me know how i can display only distinct records.
The most likely reasons could be that Distinct when called with no parameter by default compares all the public properties for equality. I suspect your Id is going to be unique. Hence the Distinct is not working for you.
You can try something like
myCustomerList.GroupBy(product => product.Products_Id).Select(grp => grp.First());
I found this as answers to
How to get distinct instance from a list by Lambda or LINQ
Distinct() with lambda?
Have a look at LINQ Select Distinct with Anonymous Types
I'm guessing r.ID is varying between the two products that are the same, but you have the same Products_Id?
You can write an implementation of IEqualityComparer<CustomerProduct>. Once you've got that, then you can use this:
DbContext.CustomerProducts.Where(p => p.Customers_Id == customerId)
.ToList()
.Distinct(new MyComparer())
.Select(r => new {
// etc.
public class MyComparer : IEqualityComparer<CustomerProduct>
{
// implement **Equals** and **GetHashCode** here
}
Note, using this anonymous comparer might work better for you, but it compares all properties in the anonymous type, not just the customer ID as specified in the question.

In LINQ, how can I do an .OrderBy() on data that came from my .Include()?

Here's what I'm doing:
List<Category> categories =
db.Categories.Include("SubCategories").OrderBy(c => c.Order).ToList();
I have a column on my categories table called "Order" which simply holds an integer that gives the table some kind of sorting order.
I have the same column on my "SubCategories" table...
I want to know the simplest solution to add the sort on my subcategories table... something like:
List<Category> categories =
db.Categories.Include("SubCategories").OrderBy(c => c.Order)
.ThenBy(c => c.SubCategories as x => x.Order).ToList();
I'd like to keep it in this type of LINQ format... (method format)...
Keep in mind, i'm working in MVC and need to return it to a view as a model. I've been having trouble with errors because of AnonymousTypes...
I'm not sure if this is supported, but here's how it might be done:
List<Category> categories =
db.Categories.Include(c => c.SubCategories.OrderBy(s => s.Order)).OrderBy(c => c.Order)
The Include method now supports Expressions like this, but I'm not certain if it supports ordering too.
You might be better off sorting the subcategories when you use them, probably in your view.
For example:
#for (var cat in Model.Categories) {
#cat.Name
#for (var sub in cat.SubCategories.OrderBy(c => c.Order) {
#sub.Name
}
}
You can split the single query into 2 queries which just fill up the context:
IQueryable<Category> categoryQuery = db.Categories.Where(c=> /*if needed*/);
List<Category> categories = categoryQuery.OrderBy(c => c.Order).ToList();
categoryQuery.SelectMany(c => c.SubCategories)
.OrderBy(sub => sub.Order)
.AsEnumerable().Count(); // will just iterate (and add to context) all results
You even don't need the error prone string "SubCategories" anymore then.
If Category.SubCategories is a collection in itself, then you won't be able to order using the existing extension methods (and c => c.SubCategories as x => x.Order translates to almost nothing, basically saying that SubCategories is a Func<SubCategory, bool>)
If you're content to have the sorting done in memory (which shouldn't really be a problem since you're already fetching them from the database anyway, provided you don't have thousands of the things) you can implement your own custom IComparer<Category> which interrogates the SubCategories of each Category to determine whether one Category should be placed above or below another Category in a sort operation.
Your statement would then be:
var categories = db.Categories.Include("SubCategories").OrderBy(x => x, new CategorySubCategoryComparer())

Categories