DEV Community

Latz
Latz

Posted on

How to loop through an array with a variable starting element

Recently I was faced with an interesting problem. I needed to create a simplified (pseudo-) cron interface:

Image description

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)
Enter fullscreen mode Exit fullscreen mode

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++;
}
Enter fullscreen mode Exit fullscreen mode

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:

  1. the result is always less than or equal to the dividend (5)
  2. 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++;
}
Enter fullscreen mode Exit fullscreen mode

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)
Enter fullscreen mode Exit fullscreen mode

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)