Ну, главная проблема в том, что вы сбрасываете c
, как только оно становится больше payFor
.Это работает до тех пор, пока minPurchaseQuantity-payFor=1
, но в других случаях это не так.
Хотя это не так просто, как решение, представленное в моем первом ответе, я думаю, что настоящий алгоритм может быть реализован более кратко.Следующий код сначала группирует товары в группах, имеющих право на скидку.Затем для каждой из партий она пропускает до payFor
элементов и рассчитывает скидку от остальных
// first batch the items in batches eligible for discount (e.g. batches of three in take 3, pay for x)
var batchedItems = BatchItemsEligibleForDiscount(items, minPurchaseQuantity);
var discounts = batchedItems.Select(batch => batch.Skip(payFor))
.SelectMany(batch => batch) // flatten nested IEnumerable to an IEnumerable<Artible>
.Select(article => new Discount() { Value = article.UnitPrice });
. BatchItemsEligibleForDiscount
получает партии, которые имеют право на скидку (т. Е. Имеют по 3 позиции, если каждая «возьми 3, заплати за X» . Статьи с Quantity>1
«взорвались» , т. е. если количество равно 3, создаются 3 различных объекта.
IEnumerable<IEnumerable<Article>> BatchItemsEligibleForDiscount(items, minPurchaseQuantity)
{
return items.OrderByDescending(article => article.UnitPrice)
.Select(article => Enumerable.Range(1, article.Quantity).Select(n => new Article() { Quantity = 1, UnitPrice = article.UnitPrice })) // "explode" articles
.SelectMany(item => item) // flatten to an IEnumerable<Article>
.Select((article, index) => new { article, index })
.GroupBy(x => x.index / minPurchaseQuantity)
.Where(group => group.Count() == minPurchaseQuantity) // only take batches elegible for discount
.Select(group => group.Select(x => x.article));
}
См. эту скрипку для демонстрации.
СТАРЫЙ ОТВЕТ
Расчет скидки намного проще. Вы можете рассчитать числонаборов, подходящих для скидки (если взять 3, заплатить за 2 и 8 предметов, у вас есть две целые пачки по 3 предмета в каждой) .Рассчитав разницу между предметами, которые нужно взять, и предметами, которые нужно оплатить, и умножив их на количествопачками и ценой за единицу вы можете рассчитать скидку
var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount;
var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
Пример: Взять 3, заплатите за 1 с помощью 8 предметов:
numberOfDiscountableBundles = 8 / 3 = 2 (integer division!)
discount = 2 * (3 - 1) * p = 2 * 2 * p = 4 * p
Это двапакеты со скидкой по три штуки в каждой (шесть штук). не оплачиваются (только по одному на пачку), поэтому общая цена дисконтируется в четыре раза больше цены за единицу.
Вы можете заключить это в капсулу методом
Discount CalculateDiscountForArticle(Article article, int amountOfItemsElegibleForDiscount, int payFor)
{
var numberOfDiscountableBundles = article.Quantity / amountOfItemsElegibleForDiscount;
var discount = numberOfDiscountableBundles * (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
return new Discount
{
Value = discount
};
}
И ваша оригинальная функция становится такой же простой, как
var discounts = articlesForPackage.OrderByDescending(a => a.UnitPrice)
.Select(a => CalculateDiscountForArticle(a, amountOfItemsElegibleForDiscount, payFor));
РЕДАКТИРОВАТЬ НА СТАРЫЙ ОТВЕТ
Если скидка предоставляется только один раз для клиента и товара,расчет немного отличается
double discount = 0;
if(article.Quantity >= amountOfItemsElegibleForDiscount)
{
var discount = (amountOfItemsElegibleForDiscount - payFor) * article.UnitPrice;
}