Подтвердить что ты не робот

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

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

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

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

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

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

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

4b9b3361

Ответ 1

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

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

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

Ответ 2

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

$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];

Ответ 3

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

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

$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

Ответ 4

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

Ответ 5

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

$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.