Выбор ближайшего значения из массива, отражающего диапазоны

У меня есть массив, который отражает процентную скидку в зависимости от количества заказанных предметов:

$rebates = array( 1 => 0, 3 => 10, 5 => 25, 10 => 35) 

это означает, что для одного или двух предметов вы не получаете скидку; для 3+ предметов вы получаете 10%, для 5+ предметов 20%, для 10+ 35% и так далее.

Есть ли элегантный , однострочный способ получить правильный процент скидки для произвольного количества предметов, скажем, 7 ?

Очевидно, это можно решить, используя простой цикл: это не то, что я ищу. Меня интересует, есть ли основной массив или другая функция, о которой я не знаю, что может сделать это более элегантно.

Я собираюсь присудить принятый ответ на 200 тысяч, но, видимо, я должен подождать 24 часа, пока не смогу это сделать. Вопрос решен.

Вот еще один, опять же не короткий.

 $percent = $rebates[max(array_intersect(array_keys($rebates),range(0,$items)))]; 

Идея состоит в том, чтобы получить максимальный ключ ( max ), который находится где-то между 0 и $items .

Я думаю, что вышеупомянутые однострочные решения не очень элегантны или читабельны. Так почему бы не использовать то, что действительно может понять кто-то на первый взгляд?

 $items = NUM_OF_ITEMS; $rabate = 0; foreach ($rabates as $rItems => $rRabate) { if ($rItems > $items) break; $rabate = $rRabate; } 

Для этого, очевидно, нужен отсортированный массив, но по крайней мере в вашем примере это дано;)

Хорошо, я знаю, вам не нужно решение с простым циклом. Но как насчет этого:

 while (!isset($rabates[$items])) { --$items; } $rabate = $rabates[$items]; 

Все еще довольно просто, но немного короче. Можем ли мы сделать еще короче?

 for (; !isset($rabates[$items]); --$items); $rabate = $rabates[$items]; 

Мы уже приближаемся к одной линии. Итак, давайте немного обманываем:

 for (; !isset($rabates[$items]) || 0 > $rabate = $rabates[$items]; --$items); 

Это меньше, чем все подходы в других ответах. У него есть только один недостаток: он изменяет значение $items которые вам все еще понадобятся позже. Таким образом, мы могли бы сделать:

 for ($i = $items; !isset($rabates[$i]) || 0 > $rabate = $rabates[$i]; --$i); 

Это снова один символ меньше, и мы сохраняем $items .

Хотя я думаю, что последние две версии уже слишком хаки. Лучше придерживайтесь этого, так как он короткий и понятный:

 for ($i = $items; !isset($rabates[$i]); --$i); $rabate = $rabates[$i]; 

Это может работать без изменения массива.

Но массив должен быть построен по-другому, чтобы это работало

 $rebates = array( 3 => 0, //Every number below this will get this rebate 5 => 10, 10 => 25, 1000 => 35); //Arbitrary large numer to catch all $count = $_REQUEST["count"]; $rv = $rebates[array_shift(array_filter(array_keys($rebates), function ($v) {global $count; return $v > $count;}))]; echo $rv; 

Рабочий тестовый файл, просто измените счет в URL-адресе

http://empirium.dnet.nu/arraytest.php?count=5
http://empirium.dnet.nu/arraytest.php?count=10

Нет такой основной функции!

Лучшее, что я могу сделать до сих пор:

 $testValue = 7; array_walk( $rebates, function($value, $key, &$test) { if ($key > $test[0]) unset($test[1][$key]); } array($testValue,&$rebates) ); 

Использует неприятную небольшую причуду для передачи по ссылке и отбрасывает любую запись в массиве $ rebates, где ключ численно больше $ testValue … к сожалению, он все еще оставляет записи с более низким ключом, поэтому понадобится array_pop () чтобы получить правильное значение. Обратите внимание, что он активно уменьшает записи в исходном массиве $ rebates.

Возможно, кто-то может построить на этом, чтобы отбросить нижние записи в массиве.

У нас нет 5.3.3 доступного для передачи данных, поэтому он не проверяется с помощью анонимной функции, но работает (насколько это работает) при использовании стандартной функции обратного вызова.

РЕДАКТИРОВАТЬ

Основываясь на моем предыдущем однострочном слое, добавив вторую строку (так что, вероятно, ее не следует учитывать):

 $testValue = 7; array_walk( $rebates, function($value, $key, &$test) { if ($key > $test[0]) unset($test[1][$key]); } array($testValue,&$rebates) ); array_walk( array_reverse($rebates,true), function($value, $key, &$test) { if ($key < $test[0]) unset($test[1][$key]); } array(array_pop(array_keys($rebates)),&$rebates) ); 

Теперь получается массив $ rebates, содержащий только один элемент, являющийся наивысшим ключом точки останова от исходного массива $ rebates, который является более низким ключом, чем $ testValue.