Price Range class

<h1>Price Range Test</h1>


<p>
The purpose of this class is to create dynamic price ranges (minimum and maximum value ranges)
according to the grouping of prices passed through an array.
</p>

<p>
The return of the class is a list of intervals (minimum / maximum) with the
amount of items that are within each range.
</p>

<?php
$array = array(10, 50, 50, 520, 140, 551, 199, 601, 601, 650, 681, 350, 250, 851, 871, 871, 900, 990, 999, 1001, 1130, 1149, 351, 1300, 4460);
?>

<p>
The array of prices contains:
<?php
foreach($array as $item)
{
    echo $item . ', ';
}
?>
</p>

<p>
The result is:
<?php
    $t = new PriceRange($array);
    echo $t->show();
?>
</p>

<?php
class PriceRange {

    //Based on code by Emrah Toy
    private $prices;
    private $lastElement;
    private $highestPrice;
    private $minimumPrice;
    private $maxPRoductInRange;
    private $rangeChart;
    private $chart;
   
   
    function __construct($array_of_prices) {
      $this->prices = $array_of_prices;
      $this->lastElement= end($this->prices);
      $this->highestPrice = round($this->lastElement, -2);
      $this->minimumPrice = $this->prices[0];
      $this->maxPRoductInRange = 5;
      $this->rangeChart = array();
      $this->chart = array();
      $this->calculateRange();
    }
   
    private function calculateRange() {
      $this->makeRange($this->minimumPrice,$this->highestPrice);
      $count = count($this->rangeChart);
      for($a = 0; $a < $count; $a++) {
        if(isset($this->rangeChart[$a + 1])) {
          $min = $this->rangeChart[$a];
          $max = $this->rangeChart[$a + 1];
          $result = $this->countItems($min,$max);
          if($result[0] > $this->maxPRoductInRange) {
              $this->makeRange($min,$max);
              $this->calculateRange();
          }
        }
      }
    }

    private function makeRange($min=0,$max) {
        $middleOfRange=($max+$min)/2;
        $this->rangeChart[]=$min;
        $this->rangeChart[]=$middleOfRange;
        $this->rangeChart[]=$max;
        $this->rangeChart = array_unique($this->rangeChart);
        sort($this->rangeChart, SORT_NUMERIC );        
    }
   
    private function countItems($min,$max) {
      $count=0;
      $rest=0;
     
      foreach($this->prices as $price) {
        if($price >= $min && $price < $max) {
          $count++;
        }
        else {
          $rest++;
        }
      }
      $this->chart[$min] = $count;
      return array($count,$rest);
    }
   
    function show() {
      $minPrices = array_keys($this->chart);
      $count = count($minPrices);
      $line = '';
      for($a = 0; $a < $count; $a++) {
        $line .= '<br>';
        $line .= $minPrices[$a];
        $line .= isset($minPrices[$a+1]) ? ' - ' . $minPrices[$a+1] : '+';
        $line .= '('. $this->chart[$minPrices[$a]] .')';
      }
      return $line;
    }
}

?>

I was browsing the dlass given above. I am less stuck with classes terminology, but more confused with general logic.

Since functions declared are intertwined. I get stcuked in understanding of two functions to finally understand the whole class logic:

#1

    private function calculateRange() {
      $this->makeRange($this->minimumPrice,$this->highestPrice);
      $count = count($this->rangeChart);
      for($a = 0; $a < $count; $a++) {
        if(isset($this->rangeChart[$a + 1])) {
          $min = $this->rangeChart[$a];
          $max = $this->rangeChart[$a + 1];
          $result = $this->countItems($min,$max);
          if($result[0] > $this->maxPRoductInRange) {
              $this->makeRange($min,$max);
              $this->calculateRange();
          }
        }
      }
    }

and this one →

    function show() {
      $minPrices = array_keys($this->chart);
      $count = count($minPrices);
      $line = '';
      for($a = 0; $a < $count; $a++) {
        $line .= '<br>';
        $line .= $minPrices[$a];
        $line .= isset($minPrices[$a+1]) ? ' - ' . $minPrices[$a+1] : '+';
        $line .= '('. $this->chart[$minPrices[$a]] .')';
      }
      return $line;
    }

Generally, I dont think of classes as having a return; functions OF the class have returns, not the class itself. show has a return.

The two functions you have listed do not interacting with one another.
So, I shall handle each independently;

The Constructor.

The constructor defines some initial values that will be important for discussing the activities of the functions; that is to say, we can assume some preexisting values at the start of the function calls:
prices contains a copy of the array that was used to construct the object.
lastElement holds the value of the last item in the prices array.
highestPrice is the value of lastElement, rounded to the nearest hundred.
minimumPrice is the value of the first element in the prices array.
(An important note here: this constructor DOES NOT SORT THE ARRAY. Meaning that highestPrice and minimumPrice may be completely incorrect descriptors. The constructor assumes that the incoming array is sorted from lowest to highest already. A very bad assumption.)
maxPRoductInRange is arbitrarily defined to be 5.
rangeChart and chart are empty arrays.

Immediately upon construction, calculateRange.

calculateRange

the calculateRange function defines the rangeChart (see makeRange, below).
It then walks the rangeChart, and does the following:
If the element of the rangechart is not the last element, countItems from the current value to the next value in the rangeChart this returns an Array.
If the first element of that result is greater than 5, change the rangeChart to use the current value and the next value as its boundaries, and call this function again.

Note: As this is a recursive call on itself within a loop of itself, as soon as one iteration of the recursion fails the if check, the recursion loop collapses and the function returns null.

Effect: Assuming that the rangeChart contains 3 items to start with (see makeRange), the rangeChart gets reset to the smallest range such that the result of countItems is less than or equal to 5. Why? I dunno.

makeRange

makeRange simply takes the two parameters it is given (or if it is given exactly 1 parameter, it will go from 0 to that value), defines an array that contains the minimum value, the maximum value, and the mathematical average of those two values. If those two values are the same, it will return exactly 1 value in the array;
makeRange(0,1) returns [0,0.5,1], as would makeRange(1,0)
makeRange(0,0) returns [0].
(Note: Because these calculations are not rounded in any form, it will never return 2 elements, always either 3 or 1. This code could be made more efficient.)

countItems

countItems takes two parameters, and finds the number of elements in prices that are in the range [min,max).
it sets the value of chart[$min] to the number of items found. And then returns the two numbers as an array as well. Because reasons.

show

Show is not called by the constructor; it is an active function.
It does a foreach and tries to… express… the minimum values passed to the countItems function as a range. And then also echo it in parenthesis.
Why? No idea. Does it make any logical sense? No. Does it do what its designed to do efficiently? No.

The class is… frankly poorly designed, not well logic’ed, and doesn’t appear to serve any mathematical process i’m aware of.

2 Likes

Well lets start first with show(). Here we see that it is fetching the keys from an array. This array is in $this->chart. It is then simply using a count of those keys to determine how many minimum prices it is going to show. It is building up a single line to display for each minimum price. That is all it is doing.

Now calculateRange() is doing a bit more and to help us understand what it is doing, it makes a bit more sense to look at the makeRange() method. We see in that method that it is building an array consisting of a minimum, middle of the range and max value. Then it is finding the unique values of that array and sorting it. Meaning by the time this method is done, $this->rangeChart is going to consist of a sorted set of unique values. makeRange() can be called on multiple times so $this->rangeChart will most likely contain many values (in other words rangeChart isn’t cleared each time makeRange is called).

So now back to calculateRange(). Once the range of values are made ($this->rangeChart is created based on the minimumPrice and highestPrice), we are going to start looping through them. That is why you see it starting to count on $this->rangeChart. As we loop through this range of values we are taking two consecutive values (one being min and one being max) and counting items in another list called $this->prices (which is the list of prices we gave the class at the very beginning). If the number of items it finds is greater than 5 (because that is what we set as the maxPRoductInRange) then it is going to create a new range based on that min and max and recalculate. Think of this as “zeroing in” on a subset of values.

For instance… lets assume you have a list of prices… [1.00, 1.10, 1.30, 1.40, 2.00, 5.49, 6.45, 8.99, 9.00, 10.00]. Minimum price will be 1.00, highest is 10.00. calculateRange is going to first make a range based on (10.00 + 1.00) / 2 = 5.50. MakeRange will now set rangeChart to be [1.00, 5.50, 10.00]. With me so far? I hope so. Since these values are unique and sorted already, makeRange is done. Back to calculateRange.

We count this new rangeChart. In our case that is 3. We then start looping through rangeChart. Min gets set to 1.00, max gets set to 5.50 and we count the number of prices in the price list from earlier that are in between 1.00 and 5.50. That would be values [1.00, 1.10, 1.30, 1.40, 2.00, 5.49] right? Meaning the count is 6. 6 > 5, so it goes into making a new range between 1.00 and 5.50 instead of 1.00 and 10.00 we had originally.

Once it makes the new range, it again recalculates by calling calculateRange and this time the number of prices from the prices list is not going to be more than 5.

Now as we counted our items, we stored the counts in an array called $this->chart. So when we are all done counting our ranges, we can call $this->show() to show our counts.

The one thing I will say about all this is one simple idea about commenting. This scenario is the very reason we comment. You can look at this code and while the naming of things are not horrible (could be better in places) it is doing many different things and relying on certain values being set before other operations can be done. A few cleverly placed and descriptive comments could save a ton of confusion. I am mentioning this to all those people who say we should not comment things and that code should be “purely self describing”. Make sure to comment, comment in moderation and comment when it makes sense to describe what is truly going on. Make comments count!

2 Likes

Thanks, Can you please explain this line:

$line .= isset($minPrices[$a+1]) ? ' - ' . $minPrices[$a+1] :

Arent there 3?
min, average and maximum

examole:
min= 2
max=10
average = 5 then

That’s a basic ternary operation, isn’t it? If the $minPrices[$a+1] element is set, then concatenate a “-” and the value of that array element to $line, otherwise do whatever comes after the : which is not in your quoted code, concatenate a “+” symbol in the post earlier.

2 Likes

the word chart is also quite delusional when there is/are no real visual charts.

But notice that it is taking two CONSECUTIVE values… rangeChart[$a] which is min and rangeChart[$a + 1] which is max. That is per loop. So the next iteration of the for loop, it takes rangeChart[$a] (which was last iterations $a + 1) and the new $a + 1. Yes three values, but it is only take two at a time.

1 Like

false. in the first loop, $a is the min, and $a+1 is the mathematical average. MakeRange(a,b) will make rangeChart [a,(a+b)/2,b], unless a =b, in which case it makes it [a].

But show() isnt looking at rangeChart, it’s looking at chart, which is… an array of extremely odd values.

So lets say your array was
[1,2,3,4,5,6,100]

Thing starts, defines the rangeChart to be [1,50.5,100], counts the items between 1 and 50.5.
Sets chart[1] to 6, if evaluates as true; rangeChart now becomes [1,25.25,50.5], and repeats. It does that a few times, until the range becomes [1,3.03125,6.0625]. At that point, chart[1] becomes 3, and the loop collapses.
It then runs the for loop again, but this time rangeChart[$a] is 3.03125 and rangeChart[$a+1] is 6.0625.
chart[3.03125] gets set to 3.

Show will then output:

1-3.03125: 3
3.03125: 3

What does that mean? I have no idea.

I think it’s trying to split an array up into pieces that contain no more than X elements? (In which case, sort, chunk, call it a day…not sure why we need the class other than hey-ho we wanted to write a class?)

1 Like

I have no problem in understanding the logic of the ternary operator:

condition ? true : false

I am still stuck in this function:

function show() {
      // Array Key return a new arrray containing keys
      $minPrices = array_keys($this->chart);
      $count = count($minPrices);
      $line = '';
      for($a = 0; $a < $count; $a++) {
        $line .= '<br>';
        $line .= $minPrices[$a];
        $line .= isset($minPrices[$a+1]) ? ' - ' . $minPrices[$a+1] : '+';
        $line .= '('. $this->chart[$minPrices[$a]] .')';
      }
      return $line;
}

what is the mathematical logic behind this:

$line .= isset($minPrices[$a+1]) ? ' - ' . $minPrices[$a+1] : '+';

“If this value is not the last in the array, write the next value in the array down with a hyphen.”

Not entirely sure what the + is meant to be for. I assume the hyphen is meant to indicate a range.

1 Like

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.

This topic was automatically closed 90 days after the last reply. New replies are no longer allowed.