skip to Main Content

I would like to apply discounts according to a given rule:

  • If the quantity is greater than 10, apply a 5% discount
  • If the quantity is greater than 20, apply a 10% discount
  • If the quantity is greater than 30, apply a 15% discount

I have expressed said rule in one array.

But I don’t know how to verify that only the highest possible condition is met.

For example, if the amount is greater than 30, apply only 15%.

Any idea how to solve this or improve the logic?

Code

$rules=array(10=>5, 20=>10, 30=>15);
$qty=array(
    array('qty'=>1, 'price'=>100),
    array('qty'=>2, 'price'=>9),
    array('qty'=>10, 'price'=>54),
    array('qty'=>13, 'price'=>22),
    array('qty'=>21, 'price'=>8),
    array('qty'=>22, 'price'=>999),
    array('qty'=>35, 'price'=>2),
    array('qty'=>50, 'price'=>1),
    
);

print("qtytpricetfinalPricen");

foreach ($qty as $item) {
    $finalPrice=$item['price'];
    $discount="";

    foreach ($rules as $k=>$v) {
        if($item['qty'] >= $k) {
            $finalPrice= $finalPrice - ($finalPrice * ($v / 100));
            $discount.="*";
        }
    }    
    printf("%st%st%s%sn",$item['qty'],$item['price'],$finalPrice,$discount);
}

OutPut:

  qty   price   finalPrice
  1     100     100
  2     9       9
  10    54      51.3*
  13    22      20.9*
  21    8       6.84**
  22    999     854.145**
  35    2       1.4535***
  50    1       0.72675***

2

Answers


  1. You could sort the $rules array from the highest key to lowest. This means if we encounter a threshold, we don’t need to do anymore iterations for the lower rule:

    uksort($rules, fn($a, $b) => $b - $a);
    

    You could rework the calculation statement slightly to be more lean, though this is more stylistic tweak:

    $finalPrice *= (1 - ($v / 100));
    

    For the discount string, I assumed that an asterisk corresponds to a multiple of 10 corresponding to the discount applied:

    $discount = str_repeat('*', round($k / 10));
    

    Lastly, we break inside the if since we do not need to process anymore rules for the current item. The saves some iteration cycles.

    Putting it all together:

    uksort($rules, fn($a, $b) => $b - $a);
    
    print("qtytpricetfinalPricen");
    
    foreach ($qty as $item) {
        $finalPrice=$item['price'];
        $discount="";
    
        foreach ($rules as $k=>$v) {
            if($item['qty'] >= $k) {
                $finalPrice= $finalPrice * (1 - ($v / 100));
                $discount = str_repeat('*', round($k / 10));
                break;
            }
        }
        printf("%st%st%s%sn",$item['qty'],$item['price'],$finalPrice,$discount);
    }
    

    Would output:

    qty price   finalPrice
    1   100 100
    2   9   9
    10  54  51.3*
    13  22  20.9*
    21  8   7.2**
    22  999 899.1**
    35  2   1.7***
    50  1   0.85***
    
    Login or Signup to reply.
  2. You’re close. Two things are missing:

    Since you test with

    if ($item['qty'] >= $k) {
    

    you need to sort the $rules array in descending order by its keys, like this:

    $rules = [ 10 => 5, 20 => 10, 30 => 15 ];
    krsort($rules);
    

    The second step is to add a break statement to the inner loop:

    foreach ($rules as $k => $v) {
      if ($item['qty'] >= $k) {
        $finalPrice -= ( $finalPrice * ( $v / 100 ) );
        // $discount .= '*';   
        break;   // Discount found, so stop testing for other discounts
      }
    }
    

    Now the output will be:

    1   100 100
    2   9   9
    10  54  51.3
    13  22  20.9
    21  8   7.2
    22  999 899.1
    35  2   1.7
    50  1   0.85
    

    The line

     $discount .= '*';
    

    is commented out because it does not make sense anymore. It showed how often a discount was applied. If the purpose is to show the size of the discount – 5 prints: *, 10 prints: **, 15 prints: *** –, then this line would produce that output:

     $discount = str_repeat('*', $v / 5);
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search