Recently I was faced with an interesting problem. I needed to create a simplified (pseudo-) cron interface:
The task is to find the next selected day.
My first attempt was a naive one. Loop to the end of the array and if nothing was found, start a second loop at the beginning and move to the current day (pseudocode, no error detection, performance optimization or edge casing):
$days = Array(false, true, false, true, false, false, false);
$today = 4 // Thursday
$i = today + 1; // loop index starts at the next day (Friday)
$next = -1; // loop abort variable
// iterate to the end of the array
while ($i <= $today.length() && $next == -1) {
if ($days[i] == true) { // day checkbox is selected
$next = $days[$i];
}
$i++;
}
// no truthy element was found
if ($next == -1) {
$i = 0; // start at the beginninf of the array
// iterate from the start to the current day
while ($i <= $today && $next == -1) {
if ($days[i] == true) { // day checkbox is selected
$next = $i;
}
$i++;
}
}
print $next; // 1 (Monday)
The problem can be abstracted: Printing all the indices of an array in sequence, using a variable starting point. In the example above: 5, 6, 0, 1, 2, 3, 4
.
$start = 4;
$arr = Array(false, true, false, false, true, false, false);
$max = $arr.length;
$i = $start + 1 ;
while ($i < $max) {
print ($i); // 5, 6
$i++;
}
$i = 0;
while ($i < $start + 1) {
print ($i); // 0, 1, 2, 3, 4
$i++;
}
If you look at the output, you’ll notice that the numbers are all integers and less than or equal to max
.
There’s a special, lesser-known operator that comes in handy in this case: modulo. In a nutshell, modulo returns the integer remainder of a division, e.g:
- 1 mod 5 → 1 (0 remaining 1)
- 2 mod 5 → 2 (0 remaining 2)
- 3 mod 5 → 3 (0 remaining 3)
- 4 mod 5 → 4 (0 remaining 4)
- 5 mod 5 → 0 (1 remaining 0)
- 6 mod 5 → 1 (1 remaining 1)
- 7 mod 5 → 2 (1 remaining 2)
You will notice two things:
- the result is always less than or equal to the dividend (
5
) - the result is repeated after the dividend (
1, 2, 3, 4, 5, 6, 7
) exceeds the value of the divisor (5
).
That’s exactly what we need. Let’s put something like this, with different numbers, into code:
$start = 4;
$max = 7;
$i = $start;
while ($i < $max + $start) {
print ($i); // 4, 5, 6, 7, 8, 9, 10
print ($i mod $max); // 4, 5, 6, 0, 1, 2, 3
$i++;
}
The index i
exceeds the length of the array, but we’re using i mod max
and not the loop index itself. The result makes a round trip after reaching max and starts again at 0
. All indexes of the array are covered, starting from the middle index 4
.
The actual code now looks like this:
$days = Array(false, true, false, true, false, false, false);
$today = 4 // THursday
$i = $today + 1;
$max = $days.length;
$next = -1;
while ($i < $max + $today && $next == -1) {
if ($days[$i mod $max] == true) {
$next = i mod $max;
}
$i++;
}
print ($next); // 1 (Monday)
This code may not look as readable as before, so why are we doing this?
Because we can!
And it’s fun to come up with a clever solution to a problem. We’re programmers, after all.
Top comments (0)