Prevent item from showing up in string LINQ - c#

I have a list of prices that is based off of products which are in categories. I need to make sure those prices of products in sub-categories do not show up in this list, but they are. Sub-categories and categories are in the same table in the db, the only difference is that a sub-category will have a parent ID to show that it's a child.
I have these 2 LINQ statements that I need to tie together somehow and I can't get it right. Maybe I am not trying to do this the right way, but I'm not very experienced with LINQ yet and I haven't been able to find much help online.
To be clear, this is not for removing duplicates, I don't have that problem, I just need to prevent some of the results from showing up.
var subCategories = siteCategory.SiteSubCategories.Where(x => x.Active);
string lowestPrice = siteCategory.SiteProducts.Min(x => x.Price).ToString();
I have tried to do the following
string lowestPrice = siteCategory.SiteProducts.Where(!subCategories).Min(x => x.Price).ToString();
but I get an error saying Operator '!' cannot be applied to operand of type 'IEnumerable'
I've also tried multiple other combinations of linq statements, too many to list, so I just listed the one that I tried which made the most sense to me.
Can someone help me figure out what I am doing wrong? Thanks in advance.

You should use a lambda with .Where. That part of the code should be something like
siteCategory.SiteProducts
.Where(product => !subCategories.contains(product.Category))
.Min(x => x.Price).ToString()
with the appropriate way of getting the category of a product. I guessed at product.Category, you'll need to update that piece yourself.

I don't know the details of how your data is modeled, but assuming that Product has a Category property. And that Category has a ParentId nullable property, you could do:
siteProducts
.Where(product => product.Category.ParentId == null)
.Min(product => product.Price)
.ToString();

Related

How do I obtain all the most recent elements from an IEnumerable?

I'm developing a Windows Forms application project for my university and we are using Entity Framework to store things.
It's an e-commerce type of program and now I'm struggling to find the right way to filter an IEnumerable based on the most recent ones.
What I want is to obtain all the elements from this table called prices, in which we also store older prices as a history backup.
This table has the ID of the article that refers to, the same for the corresponding prices list, a public, and a cost price, the updated date that is the moment it was created/updated.
I have tried using many expressions but ultimately failed miserably, sometimes I brought me only the ones within a certain price list or none at all or just one.
Again, I need it to work for a function that lets you update your prices based on parameters. For example, all articles and all price lists. For that, I want only the ones that are up to date so I won't touch the history of prices.
Example of what it should return:
Thank you very much!
Update: What I have tried didn't work, in fact, I couldn't even find sense in the code I wrote, that's why I didn't post it in the first place. I guess this problem ended my brain and I can't think properly anymore.
I tried some answers that I found here. For example:
// This is an IEnumerable of the price DTO class, which has the same properties as the table.
// It contains all the prices without a filter.
var prices= _priceService.Get();
// Attempt 1
var uptodatePrices= prices.GroupBy(x => x.ArticleId)
.Select(x => x.OrderByDescending(s => s.Date).FirstOrDefault());
// Attempt 2
uptodatePrices = prices.Select(x => new PriceDto
{
Date = prices.Where(z=> z.Id == x.Id).Max(g=>g.Date)
});
Ok, It sounds like you want to return the latest price for a combination of price list and article..
You're on the right path with your first attempt, but not quite there. The second attempt looks like pure frustration. :)
I believe the solution you will be looking for will be to group the products, then take the latest price for each group. To do that you need to use the values that identify your group as the group by expression, then sort the grouped results to take your desired one.
var uptodatePrices= prices.GroupBy(x => new { x.ArticloId, x.ListPrecioId} )
.Select(x => x.OrderByDescending(p => p.Date).First())
.ToList();
When you do a GroupBy, the value(s) you specify in the groupby expression become the "Key" of the result. The result also contains an IEnumerable representing the items from the original expression set (prices) that fit that group.
This selects the Price entity, you can change the Select to select a DTO/ViewModel to return, populated by the price instead as well.
In your case you were grouping by just the ArticloId, so you'd get back the latest entry for that Article, but not the combination of article and list price. In the above example I group by both article and list price, then tell it to Select from each group's set, take the latest Price record. I use First rather than FirstOrDefault as because I am grouping on combinations I know there will be at least 1 entry for each combination. (or else there would be no combination) Avoid using ...OrDefault unless you're sure, and are handling that no result may come back.
What you are working with are LINQ queries. If you only need to sort by most recent date, you can do that like this:
prices.OrderByDescending(price=>price.FechaActualizacion).ToList();
Make sure your Price model has the FechaActualizacion property.

Linq: Get Item which is in a list which also is in a list

I'm currently trying to get a list of products which are in a list of stores but only if the product name is the same.
I always get 0 Items back.
I tried to solve to problem using two different approaches, which are below.
//First Approach, return 0
var stores= Store.ReadAll().Where(prods =>
prods.Products.Contains(product))
//Second Approach, doesn't compile but it shows what i wan't to do.
var stores= Store.ReadAll().Where(prods =>
prods.Products.Where(p => p.ProductName == productName));
Help appreciated :)
What you are looking for is Any instead of Where:
var products = Store.ReadAll().Where(prods => prods.Products.Any(p => p.ProductName == productName));
So i partly solved my problem and apparently, it wasn't really a linq problem, it was a database problem. I have a List of objects which contains a list of objects, which gives me a M:M relation. But the .net Entity Framework didn't regonize when i changed the list using list.add(item). So my list was always empty.
But anyway, thanks for the help ! :)
Assuming stores is an IEnumerable type, then following should work. It's important to pass the second parameter to Contains method, so comparision is not case sensitive, else it will return nothing if case is different between product names.
I have assumed in my answer that Store type has a property called Products of List< string > type.
var matchingStores = stores.Where(s=> s.Products.Contains(productName,
StringComparer.OrdinalIgnoreCase));

Linq Select many where property x exists in externalList

Poor wording I know, so I will be clear here..
I have
List<int> relevantIDs; //self explanitory
Now I am trying to select a list of objects from the database where their IDs exist within the list above... I cant seem to figure out the linq...
dbContext.objs.where(x => x.id ....).toList();
//I cant figure out the .... i was thinking there was an "in" but got no where...
can someone point me to an article or provide me a sample that will accomplish what I need. The list would be too big to just retrieve them all then filter down.... and hitting the database repeatedly would be not optimal in this case...
You want to find the IDs from the database that are contained within your collection of "relevant IDs", so this should work:
dbContext.objs.Where(x => relevantIDs.Contains(x.id)).ToList();
You are looking for Contains
dbContext.objs.Where(x => relevantIDs.Contains(x.id)).ToList();
This will be translated as IN clause into SQL.

query a sub-collection of a collection with linq

I've got a collection of products, and each product object has it's own ProductImages collection. Each ProductImage object has a IsMainImage bool field. I'm having a hard time building a Linq query like this:
select products.productimages.imagename where products.productid == 1 and
product.productimages.ismainimage == true
Can anyone help me figure this out, point me to an online resource where I can learn about writing linq queries like this, or both?
Thank you for your help!
Try something like
from product in products
where product.productid == 1
from image in product.productimages
where image.ismainimage
select image.imagename
I also found this list of 101 linq queries which may contain good information for you.
You can also use .SelectMany() projection method also.
products.Where(product => product.productid == 1)
.SelectMany(product =>
product.productimages.Where(image => image.ismainimage)
.Select(image => image.imagename)
);
Another way to write the query is to select first image that is the main image for the product 1:
var q = from p in products
where p.ProductID == 1
select p.ProductImages.First(img => img.IsMainImage);
I would think this is more readable than nested from clauses (which are usually used for joins and similar constructs). Using First may be also more efficient, but that's just a guess (and it very likely doesn't matter)

LINQ Select() function throws out loaded values (loadoptions)

I have a model where a Product can have multiple PriceDrops. I'm trying to generate a list of products with the most recent price drops.
Getting the most recent price drops with the products loaded is easy enough, and I thought it would be the best way to start:
dlo.LoadWith<PriceDrop>(pd => pd.Product);
db.LoadOptions = dlo;
return db.PriceDrops.OrderBy(d=>d.CreatedTime);
Works great for a list of recent price drops, but I want a list of products. If I append a ".Select(d=>d.Product)" I get a list of Products back - which is perfect - but they are no longer associated with the PriceDrops. That is, if I call .HasLoadedOrAssignedValues on the products, it returns false. If I try to interrogate the Price Drops, it tries to go back to the DB for them.
Is there a way around this, or do I have to craft a query starting with Products and not use the Select modifier? I was trying to avoid that, because in some cases I want a list of PriceDrops, and I wanted to re-use as much logic as possible (I left out the where clause and other filter code from the sample above, for clarity).
Thanks,
Tom
Try loading the Products, ordered by their latest PriceDrop:
dlo.LoadWith<Product>(p => p.PriceDrops);
db.LoadOptions = dlo;
return db.Products.OrderBy(d => d.PriceDrops.Max(pd => pd.CreatedTime));
I understand from your question that you're trying to avoid this, why?
I think what you need here is the the AssociateWith method, also on the DataLoadOptions class.
dlo.AssociateWith<Product>(p => p.PriceDrops.OrderBy(d=>d.CreatedTime))

Categories

Resources