Whats the best type of loop?

I have a # which can be 1-20 and im trying to make a variable equal to 1,2, or 3 depending on which number is selected.
So
1 would equal 1
2 would equal 2
3 would equal 3
4 would equal 1
5 would equal 2
6 would equal 3
7 would equal 1
8 would equal 2
9 would equal 3
10 would equal 1
11 would equal 2
12 would equal 3

up to 20

so something like

switch ($circuit) {
  case 1:
  $leg = '1';
  break;
  case 2:
  $leg = '1';
  break;
  case 3:
  $leg = '2';
  break;			  
  case 4:
  $leg = '2';
  break;
case 5:
$leg = '3';
break;
case 6:
$leg = '3';
break; 
...
}

would work (but ineficient)
Would this be a better way?

 $leg = ($circuit % 3) + 1; 

60 lines of code, or 1 simple math function.

I mean… do you really have to ask?

(also there’s no need for +1. 1 % 3 = 1.)

1 Like

oh, so 4%3=1,
modulo is pretty nifty

Modulo is “The remainder after division” (it’s a bit more complex than that, but thats the simple way to think about it.)
7 % 3 = 7 / 3 = 2 R 1, so return 1.

1 Like

is this right?

1%3 = 1
2&3 = 2
3%3 = 0
4%3 = 1
5&3 = 2
6%3 = 0

so the result is 1,2,0,1,2,0…
but im trying to get 1,2,3,1,2,3…

according to

I should use

($num++ % 3) + 1;

whats it do?

oh sorry, right. the inversion point’s backwards. ((x-1) % 3)+1.

((x+a) % b) + c:
x is your input variable.
a offsets the list
b determines the reset point,
c manipulates the values.

It’s a bit like moving a parabola, if you did that in your maths classes.

so:
we have an X from 1 to infinity; we have a B that is 3 if we simply did x%3:
1 = 1
2 = 2
3 = 0
4 = 1
5 = 2
6 = 3

desired output is 1 2 3 1 2 3. value at the reset point is “1”, which means we have added 1 to all values in the actual list (0+1 1+1 2+1 0+1 1+1 2+1), so factor out the 1.
x % 3 +1 gives us 2 3 1 2 3 1. Right numbers, wrong alignment. We need to shift the pattern to the right by 1, so we subtract 1 from the input:
(x-1) % 3 + 1 gives us 1 2 3 1 2 3. We now match desired output.

1 Like

@lurtnowski,

I’m not sure of the complete problem and can only suggest taking a step back to analyze once again and try and make better use of the PHP language.

Zero is considered false and all other values considered positive. Also arrays have a zero as the starting index.

Why does the circuit variable not start at zero and must the result be either 1, 2 or 3?

What is the real problem you are trying to solve by doing this?

ok, so heres the problem.

I have a box with a bunch of circuit breakers on it, each circuit breaker is on a circuit (blue numbers). Each circuit breaker would also be assigned a leg (the red numbers which are grouped by 3 (1,2,3,1,2,3,1,2,3…)


Id like to assign the leg the circuit breaker is on depending on its circuit (so a circuit breaker on circuit 7 or 8 would be assigned to leg 1)

I would be tempted to write a function so that it can be easily called, amended and tested:

<?php declare(strict_types=1);


// Test
$tmp = 'width: 88%; margin: 2em auto; text-align: center;';

echo "<table style='$tmp' >";
  echo '<tr><th> $circuit </th> <th> $result </th> </tr>';

for( $circuit =0; $circuit<20; $circuit++):
  
  $result = getResult($circuit);

  echo '<tr><td>' .$circuit .'</td><td>' .$result .'</td></tr>';
endfor;

echo '</table>';

//=====================================================
function getResult
(
  int $circuit
)
: int // $result
{
  $result = $circuit % 3 ;

  return ++$result;
}

Output:

dang, I wish I could do that, made a few changes and this is what I came up with

<?php declare(strict_types=1);


// Test
$tmp = 'width: 88%; margin: 2em auto; text-align: center;';

echo "<table style='$tmp' >";
  echo '<tr><th> $circuit </th> <th> $result </th> </tr>';

for( $circuit =1; $circuit<20; $circuit++):
  
  $result = getResult($circuit);

  echo '<tr><td>' .$circuit .'</td><td>' .$result .'</td></tr>';
endfor;

echo '</table>';

//=====================================================
function getResult
(
  int $circuit
)
: int // $result
{
  $result = (($circuit - 1) % 3);

  return ++$result;
}


the result


Perfect…

2 Likes

Note: This ISNT the math that you asked for, or what you’ve accepted as the answer. What you seem to be indicating in your drawing is a pattern of:

1 1 2 2 3 3 1 1 2 2 3 3

which is slightly different.

More accurately, what you’re trying to say is that if circuit numbers are distributed row-first, what row is a given circuit on, modulo the number of legs, 1-indexed.

function getLeg($circuit,$numberofcolumns = 2, $numberoflegs = 3) {
  //Handle bad input.
  if(!is_int($circuit) || !is_int($numberofcolumns) || !is_int($numberoflegs) || $numberofcolumns < 1 || $circuit < 1 || $numberoflegs < 1) { return 0; }
  return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1; 
}

EDIT: tweaking my defaults.

Version-002 - Not as dynamic as @m_hutley’s but it works :slight_smile:

<?php declare(strict_types=1);

// Test BEGIN
   $tmp = 'width: 88%; margin: 2em auto; text-align: center;';

   echo "<table style='$tmp' >";
     echo '<tr><th> $circuit </th> <th> getResult() </th><td> getLeg() </tr>';

     for( $circuit =1; $circuit<20; $circuit++):
     
       $res_1 = getResult($circuit);
       $res_2 = getLeg($circuit,$numberofcolumns = 2, $numberoflegs = 3) ;

       echo $tmp = <<< ____EOT
        <tr>
         <td> $circuit </td>
         <td> $res_1   </td>
         <td> $res_2   </td>
         </tr>
____EOT;
     endfor;
   echo '</table>';
// Test END

//=====================================================
function getLeg($circuit,$numberofcolumns = 2, $numberoflegs = 3) {
  //Handle bad input.
  if(!is_int($circuit) || !is_int($numberofcolumns) || !is_int($numberoflegs) || $numberofcolumns < 1 || $circuit < 1 || $numberoflegs < 1) { return 0; }
  return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1; 
}


//=====================================================
function getResult
(
  int $result
)
: int // $result
{
  // ENSURE RESULT IS EVEN
  if($result % 2) :
    ++ $result ; 
  endif;

  // GET MODULO 6 RESULT OR 3
  if( $result = $result % 6) :
    $result = $result / 2;
  else:
    $result = 3;
  endif;  

  return $result;
}

Output

$circuit 	getResult() 	getLeg()
1 	1 	1
2 	1 	1
3 	2 	2
4 	2 	2
5 	3 	3
6 	3 	3
7 	1 	1
8 	1 	1
9 	2 	2
10 	2 	2
11 	3 	3
12 	3 	3
13 	1 	1
14 	1 	1
15 	2 	2
16 	2 	2
17 	3 	3
18 	3 	3
19 	1 	1 
1 Like

Why not type hint? Would prevent a lot of checks.

i.e.

function getLeg(int $circuit, int $numberofcolumns = 2, int $numberoflegs = 3) {
    if ($numberofcolumns < 1 || $circuit < 1 || $numberoflegs < 1) {
        // Ideally I'd do this in three different checks with useful exception messages, but for the sake of bevity...
        throw new InvalidArgumentException('All arguments to getLeg() must be strictly positive');
    }

    return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1; 
}
1 Like

Because I was writing code that won’t throw errors? shrug (It doesnt actually prevent any checks, it’s just hiding them in the system, which is fair enough)

Version_003 - using only arithmetic operators and no PHP functions

<?php declare(strict_types=1);

// Test BEGIN
   $tmp = 'width: 88%; margin: 2em auto; text-align: center;';

   echo $thd = <<< ____EOT
     <table style='$tmp'>
       <tr>
         <th> \$circuit </th> 
         <th> jb()  </th>
         <td> m_h() </td>
         <td> rpk() </td>  
       </tr>
____EOT;

     for( $circuit =1; $circuit<20; $circuit++):
     
       $res_1 = getResult($circuit, $numberofcolumns = 3);
       $res_2 = getLeg   ($circuit, $numberofcolumns = 3, $numberoflegs = 12) ;
       $res_3 = getLeg2  ($circuit, $numberofcolumns = 3, $numberoflegs = 12) ;

       echo $tmp = <<< ____EOT
        <tr>
         <td> $circuit </td>
         <td> $res_1   </td>
         <td> $res_2   </td>
         <td> $res_3   </td>
         </tr>
____EOT;
     endfor;
   echo '</table>';
// Test END

//=====================================================
function getLeg
(
  $circuit,
  $numberofcolumns = 2, 
  $numberoflegs = 4
) {
  // Handle bad input.
  if(!is_int($circuit) || !is_int($numberofcolumns) || !is_int($numberoflegs) || $numberofcolumns < 1 || $circuit < 1 || $numberoflegs < 1)
  { 
    return 0; 
  }

  return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1; 
}

//=====================================================
function getLeg2
(
  int $circuit, 
  int $numberofcolumns = 4, 
  int $numberoflegs = 4
) {
    if ($numberofcolumns < 1 || $circuit < 1 || $numberoflegs < 1) {
        // Ideally I'd do this in three different checks with useful exception messages, but for the sake of bevity...
        throw new InvalidArgumentException('All arguments to getLeg() must be strictly positive');
    }

    return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1; 
}//

//=====================================================
function getResult
(
  int $result,
  int $cols = 3
)
: int // (1..$cols)
{
  // IS RESULT LESS THAN MAX RANGE
  if($new = $result % $cols) :
     $new = $result + $cols - $new; 
  else:  
     $new = $result;
  endif;

  return $new / $cols;
}//

Output: with three columns

I must admit I had a fair amount of trouble understanding what getResult was doing at first, but thats a neat way of doing it.

$result and $cols being unbounded will mean there’s a potential for misuse ($cols = 0, for example), but that’s on the user really.

1 Like

woaw, another way (simplier)
Is just to create an array of all the leg values (there will always be 20 circuits)
then just do an array lookup, is that ok?

$legs = array(1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2,3,1,2);

$leg = $legs[$circuit - 1];

See, information we didn’t have…
Personally, i’d do it a lazy way ($legs = str_repeat("123",7);), because you can index a string like an array. But then you’d have to convert to int as well during the pull.

1 Like

I wrote the function in a hurry and should have paid more attention to meaningful variable names.

The logic behind the function was gained from the image. Two important factors were the maximum values of the columns and the row number (legs).

Supposing there were five $columns then the tripping points would be 5, 10, 15, 20.

When the $circuit values are equal to any of the tripping points then the modulus would be zero or
FALSE. Therefore the result $row = $circuit / $rows.

When $modulus is positive then maximum tripping point is calculated from the following:

$max = $circuit + $cols - $mod;

/=====================================================
function getResult
(
  int $circuit, // 1..20
  int $cols = 3 // MAX RANGE
)
: int // $row 1..3
{
  // IS RESULT LESS THAN MAX RANGE
  if($mod = $circuit % $cols) :
     $max = $circuit + $cols - $mod; 
  else:  
     $max = $circuit; 
  endif;

  return $row = $max / $cols;
}//

Edit:

I still cannot understand the logic behind the return value of your function :frowning:

return floor(($circuit-1) / $numberofcolumns) % $numberoflegs + 1;