从反映范围的数组中选取最接近的值

Picking the nearest value from an array reflecting ranges

提问人:Pekka 提问时间:10/27/2010 最后编辑:Pekka 更新时间:3/28/2011 访问量:1644

问:

我有一个数组,它根据订购的商品数量反映回扣百分比:

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

这意味着对于一两件商品,您不会获得回扣;对于 3+ 项目,您将获得 10%,对于 5+ 项目,您将获得 20%,对于 10+ 35%,依此类推。

比如说,有没有一种优雅的单行方法可以为任意数量的商品获得正确的回扣百分比?7

显然,这可以使用一个简单的循环来解决:这不是我要找的。我感兴趣的是,是否有核心数组或其他我不知道的函数可以更优雅地做到这一点。

我将奖励接受的答案 200 的赏金,但显然,我必须等待 24 小时才能做到这一点。问题解决了。

PHP 数组

评论

3赞 Gordon 10/27/2010
您知道,当您不指定最大行长时,要求单行是没有意义的;)
0赞 nerkn 10/27/2010
键不在模式中?因此,我们不能做一个函数
1赞 tplaner 10/27/2010
我认为这应该成为一个维基。
1赞 NikiC 10/30/2010
@evolve:如果您认为这应该是 CW,请标记以引起 mod 的注意并告诉他们将其设置为 CW。此外,我不明白为什么这应该是 CW ;)
0赞 Thariama 10/27/2010
没有这样的核心功能!

答:

1赞 Mark Baker 10/27/2010 #1

到目前为止,我能做到的最好的:

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

2赞 David Mårtensson 10/27/2010 #2

这可能在不更改返利数组的情况下起作用。

但是数组必须以另一种方式构造才能使其工作

$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

评论

0赞 Pekka 10/27/2010
非常好,谢谢。我接受 @salathe 的方法,因为它有点短,并且在没有全局/闭包的情况下工作,但这也可以正常工作。
16赞 salathe 10/27/2010 #3

这是另一个,同样一点也不短。

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

这个想法基本上是获得介于 和 之间的最高键 ()。max0$items

评论

0赞 Pekka 10/27/2010
我接受这个,因为它是非破坏性的,是最短的方法,并且不需要全局变量/闭包。谢谢!
5赞 NikiC 10/30/2010 #4

我认为上面的单行解决方案并不是很优雅或可读。那么,为什么不使用一些乍一看就能真正理解的东西呢?

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