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!

  • 1
    Possible duplicate of Style for control flow with validation checks – gnat Sep 8 '18 at 20:17
  • 3
    @gnat: I'm afraid the linked answer won't help much here. Look closely at the conditions: they shouldn't be there in the first place. It's a basic mathematical problem (clearly a homework problem given to students). The control flow is irrelevant, because it was introduced by OP as a mistake in the first place. – Arseni Mourzenko Sep 8 '18 at 20:21
  • The behavior you want is simple enough to work out by hand. Write up a table of values for $total_magazines and $total_amount. Run your current code and be sure it does everything you predicted. Only make a simpler version after that. Now you can check that simplifying didn't change what it does. If that seems like a lot of work, look up unit testing. It lets you write code to test all this for you. What you're doing here is called refactoring. – candied_orange Sep 9 '18 at 0:05
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.

  • Hi! Thank you very much for shedding some light to my question. I was able to simplify it a little bit for your first part of advice by doing this: $calc_magazines = $total_magazines / 10; – SpookyBoogy Sep 8 '18 at 20:58
  • After this, I was able to use ceil() to simplify the previous conditions. I'm not exactly sure if this first part is what you're looking for... but I'm currently trying to figure out the next part of the question. – SpookyBoogy Sep 8 '18 at 21:05
  • @SpookyBoogy: excellent, ceil is the one. I edited the second part to make it more explicit and added a third one I missed when writing the original answer. – Arseni Mourzenko Sep 9 '18 at 9:21
  • Hi! I believe I found the solution! I'll answer my own question with the solution i found. – SpookyBoogy Sep 10 '18 at 22:20
  • -1: Never change a small integer problem to floating point. That just introduces the possibility of round-off error. f – kevin cline Oct 13 '18 at 19:55
0

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

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

Not the answer you're looking for? Browse other questions tagged or ask your own question.