Reputation: 99
Given two integers (each that represent months in a year), I need to produce an array of integers that represents the inclusive range of months between them.
To get the month numbers between December and February, the input is:
$start = 12;
$finish = 2;
The expected output contains three elements: 12, 1, and 2 (because December, January, then February)
I have tried using $arraylist = range($start, $finish);
, but when the $start
value is greater than the $finish
value, then the output is an incorrect descending range like this: [12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2]
Here are some more input-output examples:
+--------+---------+-----------------------+
| $start | $finish | $output |
+--------+---------+-----------------------|
| 3 | 11 | [3,4,5,6,7,8,9,10,11] |
+--------+---------+-----------------------|
| 11 | 3 | [11,12,1,2,3] |
+--------+---------+-----------------------|
| 3 | 3 | [3] |
+--------+---------+-----------------------+
Upvotes: 1
Views: 114
Reputation: 47864
It can be much less complicated than decrementing everything, using modulus/arithmetic, and bumping numbers into the preferred value.
Code (Demo)
$array = [];
while ($start != $finish) {
if ($start > 12) {
$start = 1;
}
$array[] = $start++; // increment AFTER pushing into the array
}
$array[] = $finish;
var_export($array);
I think this is less cryptic to read.
Inputs/Outputs:
11, 3: [11, 12, 1, 2, 3]
3, 11: [3, 4, 5, 6, 7, 8, 9, 10, 11]
3, 3: [3]
p.s. The following functional style doesn't feel silly either. There will be either 1 or 3 function calls, but again I think it is pretty easy to read AND there is no loop, no temporary variables, and no incrementing/decrementing.
Since the volume of data is guaranteed to be small, performance probably doesn't even factor in (no human is going to notice a difference in performance between an techniques on this page). It would be better to select a technique that is readable.
Code: (Demo)
$array = $start > $finish
? array_merge(range($start, 12), range(1, $finish))
: range($start, $finish);
p.p.s. A final consideration... If anyone would prefer to handle the month logic with a DateTime technique, this would also be sensible and may open a door to extending the functionality further (if the task should ever need it). The drawback is that this technique has the most overhead because it is instantiating objects to work with.
To make the output array include the last month, an easy solution is to add 1 to the finish. If the $begin
value is less than the $finish
value, then we will push the value into next year by adding 12.
Code: (Demo)
$begin = 11;
$finish = 3;
$period = new DatePeriod(
DateTime::createFromFormat('m', $begin),
new DateInterval('P1M'),
DateTime::createFromFormat('m', $finish + ($begin > $finish ? 13 : 1))
);
foreach ($period as $obj) {
$array[] = $obj->format('n');
}
var_export($array);
Upvotes: 1
Reputation: 147146
One way to do this is to use modulo 12 arithmetic. First convert $start
and $finish
to the range 0-11 by subtracting one, then increment $start
and push to the array (converting back to the range 1-12 as you do) until $start == $finish
:
$start = 12;
$finish = 2;
$arraylist = array();
$start = --$start;
$finish = --$finish;
while ($start != $finish) {
$arraylist[] = ++$start;
$start = $start % 12;
}
$arraylist[] = ++$start;
print_r($arraylist);
Output:
Array
(
[0] => 12
[1] => 1
[2] => 2
)
Upvotes: 1