0

I'm trying to figure out how to use a for loop to cycle though an array and test for two conditions. I've done it with a foreach loop, but trying to get it done with a for loop. Below is the if block and for loop that I've been working on.

        if (empty($scores[0]) ||
        empty($scores[1]) ||
        empty($scores[2]) ||
        !is_numeric($scores[0]) ||
        !is_numeric($scores[1]) ||
        !is_numeric($scores[2])) {
            $scores_string = 'You must enter three valid numbers for scores.';
            break;
    }

Here is the HTML and PHP.

HTML:

<form action="." method="post">
    <input type="hidden" name="action" value="process_scores" />

    <label>Choose action:</label><br />
    <input class="radio" type="radio" name="calculate" value="average" checked="checked">Average<br />
    <input class="radio" type="radio" name="calculate" value="total">Total<br />
    <input class="radio" type="radio" name="calculate" value="both">Both<br />

    <label>Score 1:</label>
    <input type="text" name="scores[]"
           value="<?php echo $scores[0]; ?>"/><br />

    <label>Score 2:</label>
    <input type="text" name="scores[]"
           value="<?php echo $scores[1]; ?>"/><br />

    <label>Score 3:</label>
    <input type="text" name="scores[]"
           value="<?php echo $scores[2]; ?>"/><br />

    <label>&nbsp;</label>
    <input type="submit" value="Process Scores" /><br />

    <label>Scores:</label>
    <span><?php echo $scores_string; ?></span><br />

    <label>Score Total:</label>
    <span><?php echo $score_total; ?></span><br />

    <label>Average Score:</label>
    <span><?php echo $score_average; ?></span><br />
</form>

PHP:

if (isset($_POST['action'])) {
$action =  $_POST['action'];
} else {
$action =  'start_app';
}

switch ($action) {
case 'start_app':
    $scores = array();
    $scores[0] = 70;
    $scores[1] = 80;
    $scores[2] = 90;
    break;
case 'process_scores':
    $scores = $_POST['scores'];


    // validate the scores
    for ($i = 0; $i < count($scores); $i++) {
        if (empty($scores[$i]) || !is_numeric($scores[$i])) {
        $scores_string = 'You must enter three valid numbers for scores.';
        break;
        }
    }


    // process the scores
    $scores_string = '';
    foreach ($scores as $s) {
        $scores_string .= $s . '|';
    }
    $scores_string = substr($scores_string, 0, strlen($scores_string)-1);

    // Radio buttons
    $calculate_type = $_POST['calculate'];

    switch ($calculate_type) {
        case 'average':
            $score_tally = $scores[0] + $scores[1] + $scores[2];
            $score_average = $score_tally / count($scores);
            $score_average = number_format($score_average, 2);
            break;
        case 'total':
            $score_total = $scores[0] + $scores[1] + $scores[2];
            $score_total = number_format($score_total, 2);              
            break;
        case 'both':
            $score_tally = $scores[0] + $scores[1] + $scores[2];
            $score_average = $score_tally / count($scores);
            $score_total = $scores[0] + $scores[1] + $scores[2];
            $score_total = number_format($score_total, 2);
            $score_average = number_format($score_average, 2);
            break;
    }

    break;
case 'process_rolls':
    $number_to_roll = $_POST['number_to_roll'];

    $total = 0;
    // $count = 0;
    $max_rolls = -INF;

    for ($count = 0; $count < 1000; $count++) {
        $rolls = 1;
        while (mt_rand(1, 6) != 6) {
            $rolls++;
        }
        $total += $rolls;
        $max_rolls = max($rolls, $max_rolls);
    }
    $average_rolls = $total / $count;

    break;


}

With this for loop under validate the scores in place, I yield no results when there is invalid data.

4 Answers 4

2

I would take a look at array_reduce, since that's basically what you're doing: Reducing an array to a boolean.

if( array_reduce(
   $scores,
   function($a,$b) {return $a || empty($b) || !is_numeric($b);},
   false)) {
       $scores_string = "You must enter three valid numbers for scores.";
}
Sign up to request clarification or add additional context in comments.

2 Comments

"{return $a || empty($b) || !is_numeric($b);}" how does that work?
array_reduce() will iterate all elements. $a is a copy of the cumulative result from previous iterations (an internal variable in array_reduce()). The function parameter is supposed to update it for the current iteration ($b is the current element) and return the new, updated value.
2

Since you asked for a for loop:

for ($i = 0; $i < count($scores); $i++)
    if (empty($scores[$i]) || !is_numeric($scores[$i])) {
        $scores_string = 'You must enter three valid numbers for scores.';
        break;
    }

5 Comments

I've tried your example and I get nothing. The scores are being populated by user input. All text fields share the same name attribute "scores[]". I used $scores = $_POST['scores'] to obtain the user entries. As of now with your example in place, I get no error message indicating an empty or invalid number.
I'm a bit confused. You mean you get no error message where you should? Or that you get no output using correct data? If it's the former, please check the condition inside if. If it's the latter, then yes, that's supposed to be working.
Correct! I should receive the $scores_string when the input field is blank or isNAN. Using the if block I posted above works fine, so I know there isn't a problem with the $scores_string.
You actually do receive it. You just make it '' (ie an empty string) right after the loop, using $scores_string = '';,so that setting it to the error message becomes useless...
You're welcome. Oh, and if my answer helped you solve your problem, please consider accepting it.
1

I don't understand why you testing if score is emtpy. If score is empty then is_numeric return false.

My version of for loop:

for ($i = 0, $len = count($scores); $i < $len and is_numeric($scores[$i]); $i++) {}
if ($i !== $len) echo 'You must enter three valid numbers for scores.';

edit: If you need to test if there are exactly 3 numeric items in array (as @geomagas suggest) then:

for ($i = 0; $i < 3 and is_numeric($scores[$i]); $i++) {}

// If $i is less then 3 it's mean that one of items are not set or it's not numeric
if ($i < 3) echo 'You must enter three valid numbers for scores.';

edit: Third solution which handle situation when array is not properly indexed. I'm posting this just for fun, refusing to use empty function which @geomagas forcing on me ;)

$scores = array('234', '52', '245');

for (reset($scores); $valid = is_numeric(current($scores)) and next($scores);) {}

if ( ! $valid) echo 'You must enter three valid number for scores.';

8 Comments

Please see my comment to abstr's answer, I think it applies to yours as well.
No, no, I didn't mean that. I was thinking towards the possibility of replacing empty() with isset() rather than eliminating it. What if $scores looks like this: Array([0]=>4,[2]=>9)? NOTE: I still think this is not relevant to the question.
If it will be Array([0]=>4,[2]=>9) then $i from my second example will be equal 2 and validation error will be shown. isset/empty it not necessary here.
No it wouldn't. Take a step back and think. Would execution get past your for loop? Or would php bail out spitting an error? Did you even try your own suggestion?
I assume that array is properly indexed and there is no missing "1" index. Otherwise I suggest to use foreach with if statement.
|
0

empty() should not be used since 0 is considered to be empty as well as is numeric.

$size = count($scores);
for ($i = 0; $i < $size; $i++) {
    if (!is_numeric($scores[$i])) {
        $scores_string = 'You must enter three valid numbers for scores.';
        break;
    }
}

7 Comments

Almost a good point, although that wasn't part of the question. But does is_numeric test if a variable is set?
$scores[$i] is always set, since the for loop counted the array size prior to usage.
Playing the devil's advocate, suppose $scores is Array([0]=>4,[2]=>9,[5]=>12). See also: Comments on ofca's answer
hmm.. then $scores = array_values($scores) will do it. But @geomagas aren't we hijacking @user1890525's question? :)
+1: Yes you're right. In both your points. From my side, for all it's worth, I consider the answer improved (by you, I mean). Thank you for responding to my argument.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.