Ben McCormack
Ben McCormack

Reputation: 33118

Use LINQ to count the number of combinations existing in two lists

I'm trying to create a LINQ query (or queries) that count the total number of occurences of a combinations of items in one list that exist in a different list. For example, take the following lists:

CartItems                  DiscountItems
=========                  =============
AAA                        AAA
AAA                        BBB
AAA
BBB
BBB
CCC
CCC
DDD

The result of the query operation should be 2 since I can find two combinations of AAA and BBB (from DiscountItems) within the contents of CartItems.

My thinking in approaching the query is to join the lists together to shorten CartItems to only include items from DiscountItems. The solution would be to find the CartItem in the resulting query that occurs the least amount of times, thus indicating how many combinations of items exist in CartItems.

When CartItems is filtered to only the items in DiscountItems, it can be visually displayed like this:

CartItems that get a discount
=============================
AAA    BBB    <=   This combination is eligible for a discount
AAA    BBB    <=   This combination is eligible for a discount
AAA           <=   Not eligible

Thus, because there are 2 combinations of the discount in the Cart, the result is 2.

How can this be done?


Here's the query I already have, but it's not working. query results in an enumeration with 100 items, far more than I expected.

    Dim query = From cartItem In Cart.CartItems
                Group Join discountItem
                    In DiscountGroup.DiscountItems
                    On cartItem.SKU Equals discountItem.SKU
                    Into Group
                Select SKU = cartItem.SKU, CartItems = Group

    Return query.Min(Function(x) x.CartItems.Sum(Function(y) y.Quantity))

Upvotes: 1

Views: 977

Answers (2)

Mark Byers
Mark Byers

Reputation: 838944

If I understand your question correctly you want this:

int count = DiscountItems.Min(x => CartItems.Count(item => item == x));

Result:

2

This assumes that DiscountItems cannot contain duplicates. If it can and you want a duplicate to mean that the item must appear in the cart twice to count as one discount then use this code instead:

int count = DiscountItems
    .GroupBy(d => d)
    .Min(x => CartItems.Count(item => item == x.Key) / x.Count());

I just noticed that you wanted an answer in VB.NET. I imagine that you are more easily able to translate this to VB.NET than I can as my VB.NET is not so great, but if not then leave a message and I will try running it through .NET Reflector to see if it can translate it automatically to something readable.

Upvotes: 2

tvanfosson
tvanfosson

Reputation: 532615

I think what you want is the size of the set intersection based on your description.

return Cart.CartItems.Intersect( DiscountGroup.DiscountItems ).Count()

This assumes that the items are really the same things (and they are comparable). If not, then you'd need to select out just the keys that you use to compare them and do an intersection on the keys.

 return Cart.CartItems.Select( Function(c) c.SKU )
                      .Intersect( DiscountGroup.DiscountItems
                                               .Select( Function(d) d.SKU  )
                      .Count()

Upvotes: 3

Related Questions