2010-06-28 2 views
4

이 질문은 이전의 질문 중 하나 인 Use LINQ to count the number of combinations existing in two lists과 매우 유사합니다.LINQ를 사용하여 두 목록에있는 항목의 복잡한 조합 찾기

DiscountItem에 지정된 항목을 기준으로 할인받을 수있는 CartItem 목록이 있습니다. 장바구니에서 할인을받을 수있는 품목을 꺼내서 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

까다로운 부분은 단순히 함께 두 목록에 가입 또는 조합의 수를 계산의 문제가 아니다라는 것이다. CartItem 목록에 나타나는 DiscountItem의 모든 조합에 대해 할인이 적용됩니다. 이 위의 예에서 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 개의 베이컨이 있으므로 배킹 중 하나는 할인을받지 못합니다.

2 개의 개별 목록을 열거해야하기 때문에 LINQ를 사용하여이 문제를 해결할 수 있어야한다고 생각하지만이 문제를 해결하는 데 사용할 LINQ 메서드를 생각할 수 없습니다. LINQ 쿼리의 최종 결과는 할인이 적용된 CartItem의 모음이어야합니다.

+0

당신이 당신의 카트 항목을 집계 할 수 있습니까? –

+0

@ 닉 내가 할 수 있으면 좋겠지 만.그것은 나의 첫 번째 접근이었을 것이며 분명히 문제를 훨씬 쉽게 해결할 수 있었을 것입니다. –

+1

여기 비즈니스 규칙에 대해 궁금합니다. 당신은 치즈와 "여분 베이컨"에는 할인이 적용되지 않을 것이라는 점을주의하십시오. 왜? 햄과 베이컨 모두 할인을 받는다는 점을 감안하면 규칙은 이해가 안가는 것 같습니다. 비즈니스 규칙을 명확히하면 솔루션을 공식화하는 데 도움이됩니다. – jrista

답변

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
// 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; 
    }); 
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); 
} 
}; 
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; 
     } 
    }); 
}); 
관련 문제