0

I have a bunch of if statements written in PHP for a project I'm working on. The pattern is that when the $price increases by 5 cents every time magazines are increased by 10. When the amount of magazines reaches 91, the price anything above it are fixed.

  if($total_magazines <= 10 && $total_magazines >= 1)
  {
    $price = 0.75;
    $total_amount = $total_magazines * $price;
  }
  if($total_magazines <= 20 && $total_magazines >= 11)
  {
    $price = 1.25;
    $total_amount = $total_magazines * $price;
  }
  if($total_magazines <= 30 && $total_magazines >= 21)
  {
    $price = 1.75;
    $total_amount = $total_magazines * $price;
  }

This is only a snippet of it. It's actually written all the way to

if($total_magazines > 91)
{
  $price = 5.25;
  $total_amount = $total_magazines * $price;
}

Although it does what I want it to do, but I feel I can make it a lot simpler. Is there a way to do so? Thank you very much!

3 Answers3

3

Welcome to SoftwareEngineering.SE. Since you're new here, you may be interested by reading the Open letter to students with homework problems first.

back to the problem, here are some hints to get you on the right track.

Removing the condition

Take one of the blocks:

if($total_magazines <= 30 && $total_magazines >= 21)
{
  $price = 1.75;
  $total_amount = $total_magazines * $price;
}

Inverting the order of conditions, one obtains (the code means nearly the same thing for the machine, but is slightly more readable for a human this way):

if($total_magazines >= 21 && $total_magazines <= 30)
{
  $price = 1.75;
  $total_amount = $total_magazines * $price;
}

In order to satisfy this condition, an integer should be one of those values: 21, 22, 23, ⋯ 29, 30.

What happens if you divide one of those numbers by ten? In a case of thirty, it's easy: the result is three. In a case of nine other numbers, you obtain a floating number as a result of the divisions: 2.1, 2.2, 2.3, ⋯ 2.9, 3.

PHP has a bunch of functions to work with floating point numbers. There is one of them which would, for all the numbers listed above (including the last one), give the exact same result. Could you figure out which one is that?

From your comments, it appears that you found it. You're right, ceil returns 3 for 2.1, 2.2, etc., as well as 3. The next part of the answer was edited accordingly.

Removing code duplication

Now that, based on your comments, you found that you need to use ceil, the previous piece of code can be rewritten this way:

if (ceil($total_magazines) == 3)
{
  $price = 1.75;
  $total_amount = $total_magazines * $price;
}

But wait, why would you write repetitive code again and again? in technical jargon, it's called code duplication, and there are only few cases where code duplication is fine. This is not one of them.

Let's see how we could change the code as to avoid code duplication. The goal here is to express the $total_amount for different values of ceil. There is, indeed, a simple computation which, given an integer, would give you the $total_amount.

In order to figure it out, a simple approach is to list the values (i.e. the result of ceil and the expected $price.

  • 1 → 0.75
  • 2 → 1.25
  • 3 → 1.75
  •    ⋮
  • 9 → 4.75

If you can't spot the pattern (and if the original description of the problem doesn't help), a nice reflex would be to draw those points on a piece of paper and see how it looks like. Is it a curve? A straight line? Why? Once you found it, expressing it as a mathematical function shouldn't be an issue.

Handling the edge case

Once you get the values from 1 to 90 right, there is an edge case, expressed in your question by the condition $total_magazines > 91 (by the way, there is a mistake: it's rather $total_magazines >= 91).

You can, obviously, have a conditional statement which would return a constant for any input superior or equal to 91, but going back to PHP mathematical functions, there is one which can help you to avoid the condition.

Is it a good thing to get rid of the condition? Maybe yes, maybe no. It depends on what your teacher was expecting in a specific context of this homework. In general, one would rather use a condition to make the edge case clearer, but maybe the expectation here was that the students would rely extensively on the mathematical functions in order to make an expression with no alternative control flow.

1

After going through @Arseni Mourzenko notes and advices, I was able to come with a simpler solution rather than just hard coding it. Since the price scales with the number magazines bought...

  • 1 -> 0.75
  • 2 -> 1.25
  • 3 -> 1.75
  • 4 -> 2.25... ... and so on.

    All I have to do is run a for loop.

    for($i = 1; $i <= $calc_magazines; $i++)
    {
      if($i == 1)
      {
        $price = 0.75;
      }
      else
      {
        $price += 0.5;
      }
    }
    $total_amount = $total_magazines * $price;
    

    With $calc_magazines already being ceil(), then all I have to do is give it one condition where if $i = $calc_magazines = 1 and set the price to that, any amount of magazines after that or bigger than 10, the price would increment by 0.5. Then to fufill the final condition where the magazine amount is > 91... I wrapped this for loop in an if-else statement where if($total_magazines > 91)... do its own calculation... then else{ for-loop }.

0

More concise may be to use math, specifically forced integer division via intdiv() - think "opposite of % operator".

<?php

$totalM=array(1,9,10,11,21,31,41,49,50,51,89,90,91,92,100);

foreach($totalM as $total_magazines){
    if(($total_magazines > 0) && ($total_magazines < 92)){
        $price_level=intdiv($total_magazines,10);
        $price=.75+(.5*$price_level);
    }elseif($total_magazines>91){
        $price = 5.25;
    }
    print($total_magazines." magazines price ".$price." each is ".($total_magazines * $price)."\n");
}
?>

Gives

1 magazines price 0.75 each is 0.75
9 magazines price 0.75 each is 6.75
10 magazines price 1.25 each is 12.5
11 magazines price 1.25 each is 13.75
21 magazines price 1.75 each is 36.75
31 magazines price 2.25 each is 69.75
41 magazines price 2.75 each is 112.75
49 magazines price 2.75 each is 134.75
50 magazines price 3.25 each is 162.5
51 magazines price 3.25 each is 165.75
89 magazines price 4.75 each is 422.75
90 magazines price 5.25 each is 472.5
91 magazines price 5.25 each is 477.75
92 magazines price 5.25 each is 483
100 magazines price 5.25 each is 525
ivanivan
  • 317