Created
September 7, 2016 22:57
-
-
Save vasua/5e59640db0f412c37ff80b36e74a0091 to your computer and use it in GitHub Desktop.
Functional solution of popular PHP test task
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?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