Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Picking the nearest value from an array reflecting ranges

Tags:

arrays

php

I have an array that reflects rebate percentages depending on the number of items ordered:

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

meaning that for one or two items, you get no rebate; for 3+ items you get 10%, for 5+ items 20%, for 10+ 35% and so on.

Is there an elegant, one-line way to get the correct rebate percentage for an arbitrary number of items, say 7?

Obviously, this can be solved using a simple loop: That's not what I'm looking for. I'm interested whether there is a core array or other function that I don't know of that can do this more elegantly.

I'm going to award the accepted answer a bounty of 200, but apparently, I have to wait 24 hours until I can do that. The question is solved.

like image 726
Pekka Avatar asked Oct 27 '10 11:10

Pekka


2 Answers

Here's another one, again not short at all.

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

The idea is basically to get the highest key (max) which is somewhere between 0 and $items.

like image 139
salathe Avatar answered Sep 29 '22 09:09

salathe


I think that the above one-line solutions aren't really elegant or readable. So why not use something that can really be understood by someone on first glance?

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

This obviously needs a sorted array, but at least in your example this is given ;)

Okay, I know, you don't want the solution with the simple loop. But what about this:

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

Still quite straightforward, but a little shorter. Can we do even shorter?

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

We are already getting near one line. So let's do a little bit of cheating:

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

This is shorter then all of the approaches in other answers. It has only one disadvantage: It changes the value of $items which you may still need later on. So we could do:

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

That's again one character less and we keep $items.

Though I think that the last two versions are already too hacky. Better stick with this one, as it is both short and understandable:

for ($i = $items; !isset($rabates[$i]); --$i);
$rabate = $rabates[$i];
like image 40
NikiC Avatar answered Sep 29 '22 10:09

NikiC