这个问题非常类似于我之前的问题,Use LINQ to count the number of combinations existing in two lists,除了一些进一步的曲折。
我有一个CartItem
列表,可以根据DiscountItem
列表中指定的项目获得折扣。我需要能够拉出购物车中可以获得折扣的商品并应用DiscountItem
中指定的相应折扣。折扣仅适用于存在的每个组合。以下是在应用折扣之前两个列表的样子:
BEFORE DISCOUNT: CartItems DiscountItems ============================== =========================== SKU Qty DiscountApplied SKU DiscountAmount ============================== =========================== Ham 2 $0.00 Ham $0.33 Bacon 1 $0.00 Bacon $2.00 Ham 1 $0.00 Bacon 2 $0.00 Cheese 1 $0.00 Bacon 1 $0.00
棘手的部分是,它不仅仅是将两个列表连接在一起或计算组合数量的问题。该折扣适用于DiscountItem
列表中显示的CartItem
的所有组合。在上面的示例中有3个这样的组合,如果您要在列表中迭代地应用3种组合的折扣,则每次应用折扣时数据都会如下所示:
After 1st Discount is applied: CartItems DiscountItems ============================== =========================== SKU Qty DiscountApplied SKU DiscountAmount ============================== =========================== Ham 2 $0.33 Ham $0.33 Bacon 1 $2.00 Bacon $2.00 Ham 1 $0.00 Bacon 2 $0.00 Cheese 1 $0.00 Bacon 1 $0.00 After 2nd Discount is applied: CartItems DiscountItems ============================== =========================== SKU Qty DiscountApplied SKU DiscountAmount ============================== =========================== Ham 2 $0.66 Ham $0.33 Bacon 1 $2.00 Bacon $2.00 Ham 1 $0.00 Bacon 2 $2.00 Cheese 1 $0.00 Bacon 1 $0.00 After 3rd Discount is applied: CartItems DiscountItems ============================== =========================== SKU Qty DiscountApplied SKU DiscountAmount ============================== =========================== Ham 2 $0.66 Ham $0.33 Bacon 1 $2.00 Bacon $2.00 Ham 1 $0.33 Bacon 2 $4.00 Cheese 1 $0.00 Bacon 1 $0.00
最后,除了奶酪和额外的培根之外,一切都会得到折扣。奶酪不会打折,因为它不是列表中的折扣项目。额外的培根没有折扣,因为它没有相应的火腿项目才有资格获得折扣组合。共有3个火腿和4个火腿,因此其中一个火腿不会得到折扣。
我想我应该能够使用LINQ来解决这个问题,因为它涉及枚举超过2个单独的列表,但我想不出我会用什么LINQ方法来实现这一点。 LINQ查询的最终结果应该是已应用折扣的CartItem
s的集合。
答案 0 :(得分:1)
好的,只是为了好玩,这是一种LINQ解决方案。
它可能远不及等效的迭代代码那么可读或有效,但它有效!
var discountedCart = CartItems.Select(c => c);
var combinations = DiscountItems.Any()
? DiscountItems.GroupJoin(CartItems, d => d.SKU, c => c.SKU, (d, g) => g.Sum(c => c.Qty)).Min()
: 0;
if (combinations > 0)
{
var map = DiscountItems.ToDictionary(d => d.SKU, d => combinations);
discountedCart = CartItems.Select(c =>
{
int mul;
map.TryGetValue(c.SKU, out mul);
if (mul < 1)
return c;
decimal amt = DiscountItems.Single(d => d.SKU == c.SKU).DiscountAmount;
int qty = Math.Min(mul, c.Qty);
map[c.SKU] = mul - qty;
return new CartItem { SKU = c.SKU, Qty = c.Qty, DiscountApplied = amt * qty };
});
}
foreach (CartItem item in discountedCart)
{
Console.WriteLine("SKU={0} Qty={1} DiscountApplied={2}", item.SKU, item.Qty, item.DiscountApplied);
}
(我怀疑如果你想要一个没有副作用的单一LINQ查询,那么你可以将它全部包含在Aggregate
调用中,但这将需要进一步深入的丑陋和低效率。)
答案 1 :(得分:1)
// Here we get all items have discounted, and gets minimal count of items
// This value is number of full combinations of items discounted
var minimalNumberOfItemsDiscounted =
CartItems.Where(ci => DiscountItems.Any(di => ci.SKU == di.SKU))
.GroupBy(ci => ci.SKU)
.Min(g => g.Count());
// Now we can apply discount to each item in cart, and we know how many
// times (== minimalNumberOfItemsDiscounted) discount is applied
return CartItems
.Select(ci => new
{
CartItem = ci,
Discount = DiscountItems.FirstOrDefault(di => di.SKU == ci.SKU)
})
.Select(k =>
{
if (k.Discount != null)
{
k.CartItem.Discount = minimalNumberOfItemsDiscounted * k.Discount.DiscountAmount;
}
return k.CartItem;
});
答案 2 :(得分:1)
要获得你想要的结果有点困难。你可能需要存储中间结果 - 所以需要引入一个新类。这很有挑战性,所以我按照以下方式做了 - 似乎工作
class Program {
public class CartItem {
public string sku { get; set; }
public int qty {get;set;}
public decimal DiscountApplied { get; set; }
public CartItem(string sku,int qty,decimal DiscountApplied) {
this.sku=sku;
this.qty=qty;
this.DiscountApplied=DiscountApplied;
}
}
public class DiscountItem{
public string sku {get;set;}
public decimal DiscountAmount {get; set;}
}
static List<CartItem> carts=new List<CartItem>(){
new CartItem("Ham",2,0.0m ),
new CartItem("Bacon",1,0.00m ),
new CartItem("Ham",1,0.00m ),
new CartItem("Bacon",2 ,0.00m),
new CartItem("Cheese",1,0.00m),
new CartItem("Bacon" , 1 , 0.00m )};
static List<DiscountItem> discounts=new List<DiscountItem>() {
new DiscountItem(){ sku="Ham", DiscountAmount=0.33m},
new DiscountItem(){sku="Bacon",DiscountAmount=2.0m}};
class cartsPlus
{
public CartItem Cart { get; set; }
public int AppliedCount { get; set; }
}
public static void Main(string[] args){
int num = (from ca in discounts
join cart in carts on ca.sku equals cart.sku
group cart by ca.sku into g
select new { Sku = g.Key, Num = g.Sum(x => x.qty) }).Min(x => x.Num);
var cartsplus = carts.Select(x => new cartsPlus { Cart = x, AppliedCount = 0 }).ToList();
discounts.SelectMany(x => Enumerable.Range(1, num).Select(y => x)).ToList().ForEach(x=>{cartsPlus c=cartsplus.
First(z=> z.Cart.sku==x.sku&&z.AppliedCount<z.Cart.qty);c.AppliedCount++;c.Cart.DiscountApplied+=x.DiscountAmount;});
foreach (CartItem c in carts)
Console.WriteLine("{0} {1} {2}", c.sku,c.qty, c.DiscountApplied);
}
};
答案 3 :(得分:0)
编辑:我刚刚意识到这个问题有多久了。
我个人会使用以下内容,因为它对我来说最具可读性。它并没有使用很多Linq,但我相信这是最简单的答案。
// For each discount that can be applied
foreach(var discount = DiscountedItems.Where(c => CartItems.Any(d => d.SKU == c.SKU)))
{
var discountLimit = 3; // how many items are allowed to have a discount.
foreach(var item in CartItems.Where(d => d.SKU == item.SKU))
{
if(discountLimit < item.Quantity)
{
// update the discount applied
item.DiscountApplied = discountLimit * discount.DiscountAmount;
discountLimit = 0; // causes the rest of the items to not get a discount
}
else
{
// update the discount applied
item.DiscountApplied = item.Qty * discount.DiscountAmount;
discountLimit -= item.Qty;
}
}
}
如果您有MoreLinq或LinqKit,您还可以执行以下操作:
// For each discount that can be applied
DiscountedItems.Where(c => CartItems.Any(d => d.SKU == c.SKU)).foreach(discount =>
{
var discountLimit = 3; // how many items are allowed to have a discount.
CartItems.Where(d => d.SKU == item.SKU).foreach(item =>
{
if(discountLimit < item.Quantity)
{
// update the discount applied
item.DiscountApplied = discountLimit * discount.DiscountAmount;
discountLimit = 0; // causes the rest of the items to not get a discount
}
else
{
// update the discount applied
item.DiscountApplied = item.Qty * discount.DiscountAmount;
discountLimit -= item.Qty;
}
});
});