Skip to content

Instantly share code, notes, and snippets.

@vasua
Created September 7, 2016 22:57
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save vasua/5e59640db0f412c37ff80b36e74a0091 to your computer and use it in GitHub Desktop.
Save vasua/5e59640db0f412c37ff80b36e74a0091 to your computer and use it in GitHub Desktop.
Functional solution of popular PHP test task
<?php
/**
* Есть продукты A, B, C, D, E, F, G, H, I, J, K, L, M. Каждый продукт стоит определенную сумму.
*
* Есть набор правил расчета итоговой суммы:
*
* Если одновременно выбраны А и B, то их суммарная стоимость уменьшается на 10% (для каждой пары А и B)
* Если одновременно выбраны D и E, то их суммарная стоимость уменьшается на 5% (для каждой пары D и E)
* Если одновременно выбраны E,F,G, то их суммарная стоимость уменьшается на 5% (для каждой тройки E,F,G)
* Если одновременно выбраны А и один из [K,L,M], то стоимость выбранного продукта уменьшается на 5%
* Если пользователь выбрал одновременно 3 продукта, он получает скидку 5% от суммы заказа
* Если пользователь выбрал одновременно 4 продукта, он получает скидку 10% от суммы заказа
* Если пользователь выбрал одновременно 5 продуктов, он получает скидку 20% от суммы заказа
*
* Описанные скидки 5,6,7 не суммируются, применяется только одна из них
* Продукты A и C не участвуют в скидках 5,6,7
* Каждый товар может участвовать только в одной скидке. Скидки применяются последовательно в порядке описанном выше.
*/
$MIN_COUNT_DISCOUNT_VALUE = 3;
$products = [
"A" => 10,
"B" => 20,
"C" => 15,
"D" => 13,
"E" => 17,
"F" => 18.32,
"G" => 22.3,
"H" => 25,
"I" => 14,
"J" => 14.7,
"K" => 19,
"L" => 28,
"M" => 3
];
$discount = [
"AB" => 0.1,
"DE" => 0.05,
"EFG" => 0.05,
"AK" => 0.05,
"AL" => 0.05,
"AM" => 0.05,
3 => 0.05,
4 => 0.1,
5 => 0.2,
"else" => 0.2
];
$sum = 0;
$uniqueCombinations = function ($array) {
$unique_array = array_unique($array);
$pairs = array();
array_walk($unique_array, function ($value) use (&$array,&$pairs) {
$pairs[$value] = count(array_filter($array, function ($key) use (&$value) {
return $key == $value;
}));
});
return(min($pairs));
};
$ruleSeveralGoods = function ($input,$clauses) use (&$sum,&$uniqueCombinations,&$products,&$discount) {
$array_goods = array_filter($input, function ($key) use (&$clauses) {
return in_array($key,$clauses);
});
/*
* Check if filtered array contains all value combination
*/
if (!array_diff($clauses, $array_goods)) {
$pairs = $uniqueCombinations($array_goods);
foreach ($clauses as $value) {
for ($i = 0; $i < $pairs; $i++) {
$sum += $products[$value];
foreach ($input as $key => $item) {
if ($item == $value) {
unset($input[$key]);
break;
}
}
}
}
$sum = $sum*(1 - $discount[implode($clauses)]);
};
return $input;
};
/**
* @param $input array
* @param $base string
* @param $optional array
* @return array
*
* Here we apply rule that affect always one base value in pair with one of the list of optional values.
* In requirements it was written dawn that we have to find pair from optional list for each base value
* in input array and count its value with discount.
* It was suggested that one base value from input array uses only ones to find a pair nevertheless that
* its price didn't include discount.
*/
$ruleOneOfGoods = function ($input,$base,$optional) use (&$sum,&$uniqueCombinations,&$products,&$discount) {
$array_base = array_filter($input, function ($key) use (&$base) {
return $key == $base;
});
/*
* Check if filtered array contains all value combination
*/
foreach ($array_base as $key => $value) {
foreach ($optional as $k => $v) {
if ($i = array_search($v, $input)) {
isset($discount[$value.$v])
? $product_multiplier = $discount[$value.$v]
: $product_multiplier = 0;
$sum += $products[$v] * (1 - $product_multiplier);
unset($input[$i]);
break;
}
}
$sum += $products[$value];
unset($input[$key]);
}
return $input;
};
$rulesByCount = function ($input) use (&$sum, &$products, &$discount, &$MIN_COUNT_DISCOUNT_VALUE) {
if (count($input) >= $MIN_COUNT_DISCOUNT_VALUE) {
$product_multiplier = 1 - $discount["else"];
if (isset($discount[count($input)])) {
$product_multiplier = 1 - $discount[count($input)];
};
$discount_sum = 0;
$input = array_filter($input, function ($value) use (&$discount_sum, &$products) {
$discount_sum += $products[$value];
return false;
});
$sum = $sum + $discount_sum * $product_multiplier;
}
return $input;
};
$sumOther = function ($input) use (&$sum, &$products) {
foreach ($input as $key => $value) {
$sum += $products[$value];
unset($input[$key]);
}
return $input;
};
$core = function ($input) use (&$products, &$sum, &$ruleSeveralGoods, &$ruleOneOfGoods, &$sumOther, &$rulesByCount) {
$input = $ruleSeveralGoods($input,array("A","B"));
$input = $ruleSeveralGoods($input,array("D","E"));
$input = $ruleSeveralGoods($input,array("E","F","G"));
$input = $ruleOneOfGoods($input,"A",array("K","L","M"));
$input = $rulesByCount($input);
$sumOther($input);
};
$core(array("A","B","D","M","E","A","H","D","A","B","I","J","E","E","F","G","H"));
echo "Total order amount is: " . round($sum,2);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment