C# 使用LINQ查找两个列表中项目的复杂组合

C# 使用LINQ查找两个列表中项目的复杂组合,c#,.net,vb.net,linq,C#,.net,Vb.net,Linq,这个问题与我先前的一个问题非常相似,只是有一些进一步的曲折 我有一个CartItems列表,可以根据DiscountItems列表中指定的项目获得折扣。我需要能够取出购物车中可以获得折扣的项目,并应用折扣项目中指定的适当折扣。折扣仅适用于存在的每个组合。以下是两个列表在应用折扣之前的外观: BEFORE DISCOUNT: CartItems DiscountItems =====================

这个问题与我先前的一个问题非常相似,只是有一些进一步的曲折

我有一个
CartItem
s列表,可以根据
DiscountItem
s列表中指定的项目获得折扣。我需要能够取出购物车中可以获得折扣的项目,并应用
折扣项目中指定的适当折扣。折扣仅适用于存在的每个组合。以下是两个列表在应用折扣之前的外观:

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 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的集合。

好的,只是为了好玩,这里有一种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);
}
var discountedCart=CartItems.Select(c=>c);
var组合=折扣项。任何()
? 折扣项.GroupJoin(CartItems,d=>d.SKU,c=>c.SKU,(d,g)=>g.Sum(c=>c.Qty)).Min()
: 0;
如果(组合>0)
{
var map=折扣项目。ToDictionary(d=>d.SKU,d=>组合);
折扣部分=购物车项目。选择(c=>
{
int mul;
映射TryGetValue(c.SKU,out mul);
如果(mul<1)
返回c;
十进制金额=折扣单(d=>d.SKU==c.SKU)。折扣金额;
整数数量=数学最小值(多个,c数量);
映射[c.SKU]=多个-数量;
返回新CartItem{SKU=c.SKU,Qty=c.Qty,折扣应用=amt*Qty};
});
}
foreach(折扣部分的CartItem项目)
{
Console.WriteLine(“SKU={0}数量={1}折扣应用={2}”,item.SKU,item.Qty,item.折扣应用);
}

(我怀疑,如果您想要一个没有副作用的LINQ查询,那么您可以将其全部封装在一个调用中,但这将需要进一步的丑陋和低效。)

要获得您想要的准确结果有点困难。您可能需要存储中间结果-因此需要引入一个新类。这是一个挑战,所以我做了如下-它似乎工作

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);
}
 };
类程序{
公共类商品{
公共字符串sku{get;set;}
公共整数数量{get;set;}
公开十进制折扣应用{get;set;}
公共CartItem(字符串sku、整数数量、小数折扣){
this.sku=sku;
该数量=数量;
this.DiscountApplied=DiscountApplied;
}
}
公开课折扣{
公共字符串sku{get;set;}
公共十进制折扣挂载{get;set;}
}
静态列表推车=新列表(){
新卡塔姆项目(2.0米),
新项目(“培根”,1,0.00m),
新卡塔姆项目(“1,0.00m”),
新项目(“培根”,2,0.00m),
新项目(“奶酪”,1,0.00m),
新项目(“培根”,1,0.00m)};
静态列表折扣=新列表(){
新折扣项目(){sku=“Ham”,折扣量=0.33m},
新折扣项目(){sku=“Bacon”,折扣额=2.0m};
cartsPlus类
{
公共CartItem购物车{get;set;}
public int AppliedCount{get;set;}
}
公共静态void Main(字符串[]args){
int num=(来自ca的折扣)
将购物车加入ca.sku上的购物车等于cart.sku
按ca.sku将购物车分组为g
选择new{Sku=g.Key,Num=g.Sum(x=>x.qty)}).Min(x=>x.Num);
var cartsplus=carts.Select(x=>newcartsplus{Cart=x,AppliedCount=0}).ToList();
折扣.SelectMany(x=>Enumerable.Range(1,num).Select(y=>x)).ToList().ForEach(x=>{cartsPlus c=cartsPlus。

首先(z=>z.Cart.sku==x.sku&&z.AppliedCount编辑:我刚刚意识到这个问题有多老了

我个人会使用以下内容,因为它对我来说是最可读的。它没有使用很多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;
        }
    }
}
//对于可以应用的每个折扣
foreach(var折扣=折扣数据项,其中(c=>CartItems.Any(d=>d.SKU==c.SKU)))
{
var discountLimit=3;//允许折扣的项目数。
foreach(CartItems.Where(d=>d.SKU==item.SKU)中的var项)
{
if(折扣限额<项目数量)
{
//更新应用的折扣
item.DiscountApplied=折扣限额*折扣.DiscountAmount;
折扣限制=0;//导致其他项目无法获得折扣
}
其他的
{
//更新应用的折扣
item.DiscountApplied=item.Qty*discount.DiscountAmount;
折扣限额-=物料数量;
}
}
}
如果您有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;
        }
    });
});
//对于可以应用的每个折扣
折扣数据项。其中(c=>CartItems.Any(d=>d.SKU==c.SKU)).foreach(折扣=>
{
var discountLimit=3;//允许折扣的项目数。
CartItems.Where(d=>d.SKU==item.SKU).foreach(item=>
{
if(折扣限额<项目数量)
{
//更新应用的折扣
item.DiscountApplied=折扣限额*折扣.DiscountAmount;
折扣限制=0;//导致其他项目无法获得折扣
}
其他的
{
//更新应用的折扣
item.DiscountApplied=item.Q
// 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;
        }
    });
});