-1

I have a simple PHP function that counts the number of a specific digit in an integer

function countOccurrence($number, $digit){
    $result = 0;
    while ($number > 0){
        $lastDigit = $number % 10;
        if($lastDigit === $digit){
            $result++;
        }
        $number = ($number - $lastDigit)/10;
    }
    return $result;
}

Which works completely fine on its own [Tested with countOccurrence(1111, 1) and returned 4]

But when I use it in a loop for a number range it always returns 0.

if (isset($_POST['submit'])){
    $startNum = $_POST['startNum'];
    $endNum = $_POST['endNum'];
    $findNum = $_POST['findNum'];
    $occur = 0;

    echo 'from ' . $startNum . ' to ' . $endNum . ' find how many times ' . $findNum . ' occurs';
    echo '<br>';

    $numRange = makeArray($startNum, $endNum);
    foreach ($numRange as $number){
        $test = countOccurrence($number, $findNum);
        echo 'number: ' . $number . ' find: ' . $findNum . ' countOccurrence('.$number.', '.$findNum.'): ' . $test . '<br>';
    }

    echo '<br>';
    echo '<p>The digit ' . $findNum . ' occurred ' . $occur . ' times within the range</p>';
    echo '<br>';
    echo 'countOccurrence(1111, 1) = ';
    $test = countOccurrence(1111, 1);
    echo $test;
}

The result i get from this is "number: 1 find: 1 countOccurrence(1, 1): 0" and so on

Im not really sure where its going wrong here since it appears okay when i test the function manually

Edit: Here is my complete code

<html lang="en">
    <head>
        <title>Number Counter</title>
        <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
    </head>
    <body>
        <div class="container">
            <form id="form" class="form-group" action="" method="post">
                <div class="form-group">
                    <label for="startId">Start Num</label>
                    <input type="number" name="startNum" id="startId" class="form-control">
                </div>

                <div class="form-group">
                    <label for="endId">End Num</label>
                    <input type="number" name="endNum" id="endId" class="form-control">
                </div>

                <div class="form-group">
                    <label for="findId">Find Num</label>
                    <input type="number" name="findNum" id="findId" class="form-control">
                </div>

                <input type="submit" name="submit">
            </form>
        </div>
    <script src="bootstrap/js/jquery-3.5.1.min.js"></script>
    <script src="bootstrap/js/jquery.validate.min.js"></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <script src="validate.js"></script>
    </body>
</html>

<?php

function countOccurrence($number, $digit){
    $result = 0;
    while ($number > 0){
        $lastDigit = $number % 10;
        if($lastDigit === $digit){
            $result++;
        }
        $number = ($number - $lastDigit)/10;
    }
    return $result;
}

function makeArray($startNum, $endNum){
    $numRange = array();
    for($ctr = $startNum; $startNum <= $endNum; $startNum++){
        array_push($numRange, $startNum);
    }
    return $numRange;
}

if (isset($_POST['submit'])){
    $startNum = $_POST['startNum'];
    $endNum = $_POST['endNum'];
    $findNum = $_POST['findNum'];
    $occur = 0;

    echo 'from ' . $startNum . ' to ' . $endNum . ' find how many times ' . $findNum . ' occurs';
    echo '<br>';

    $numRange = makeArray($startNum, $endNum);
    foreach ($numRange as $number){
        $test = countOccurrence($number, $findNum);
        echo 'number: ' . $number . ' find: ' . $findNum . ' countOccurrence('.$number.', '.$findNum.'): ' . $test . '<br>';
    }

    echo '<br>';
    echo '<p>The digit ' . $findNum . ' occurred ' . $occur . ' times within the range</p>';
    echo '<br>';
    echo 'countOccurrence(1111, 1) = ';
    $test = countOccurrence(1111, 1);
    echo $test;
}


Edit: Thanks for the answers! I ended up parsing the $_POST values to int instead like this to get the expected values

    $startNum = (int) $_POST['startNum'];
    $endNum =  (int) $_POST['endNum'];
    $findNum =  (int) $_POST['findNum'];
  • 1
    You will need to include your makeArray() function in your question as well. And more examples of your input data and expected results. And possibly your Form Code as you've not shown what you get for $_POST in an example. – TimBrownlaw Aug 18 '20 at 02:35
  • @TimBrownlaw possibly an error with my machine since i copied the exact same code on a different machine and got the expected results. But i will still update my code – Gabby Cervantes Aug 18 '20 at 02:43
  • You can use [`substr_count(1111, 1)`](https://www.php.net/manual/en/function.substr-count) too, is a bit easier :) – SirPilan Aug 18 '20 at 02:49
  • And [`range($startNum, $endNum)`](https://www.php.net/manual/en/function.range) instead of `makeArray`. – SirPilan Aug 18 '20 at 02:52

2 Answers2

0
function countOccurrence($number, $digit){
    $result = 0;
    while ($number > 0){
        $lastDigit = $number % 10;
        if($lastDigit === $digit){ // <-- bug is here
            $result++;
        }
        $number = ($number - $lastDigit)/10;
    }
    return $result;
}

Since you are doing a strict comparison these values are never equal. Here is why:

When you get data from $_POST it is always a string. So you are calling countOccurrence(1111, '1') which will return 0 :)

As i mentioned in the comments, you can replace your functions with these:

In case you want to stick to your functions. Convert your input to an int before passing it to your function with intval.

additional information

To prevent this from happening in the future. Have a look at this post. Its a relatively new feature of php, which was introduced for this type of scenarios.

SirPilan
  • 4,649
  • 2
  • 13
  • 26
  • Please consider (before posting a new answer) if a question can be resolved by referring to a pre-existing answer on Stack Overflow. Whenever this is possible, please do not answer the question -- instead close the page as a duplicate. If you have insights that are new and valuable to add to the pre-existing page, please post an answer there instead. – mickmackusa Aug 18 '20 at 03:48
  • Ok, ill provide this info in the post you mentioned. – SirPilan Aug 18 '20 at 03:53
  • It's probably already there. I posted two different duplicates to cover both concerns mentioned on this page: 1. the comparison 2. the counting technique. I think these pages are well covered by existing answers. Redundant answers will be flagged and deleted, so it is not worth posting unless you have something new and valuable to add. – mickmackusa Aug 18 '20 at 03:53
  • Nope its not :) – SirPilan Aug 18 '20 at 03:56
  • Which page are you referring to? The first duplicate has 21 answers (is bloated with answers -- some of which are very comprehensive). The second duplicate has 6 answers. – mickmackusa Aug 18 '20 at 03:57
  • https://stackoverflow.com/questions/80646/how-do-the-php-equality-double-equals-and-identity-triple-equals-comp/52299551#52299551 strict_types is not mentioned there, but i dont know if it should be mentioned there. – SirPilan Aug 18 '20 at 03:58
  • It is literally mentioned in the 636 upvoted and accepted answer. Here is the overarching takeaway from my comments. When you see a new basic php question, close it -- because it is going to be a duplicate question. If the old question doesn't have the best possible answer, then add it there. Stack Overflow has too much redundant content and too many people who do not research exhaustively before asking a question. – mickmackusa Aug 18 '20 at 04:02
  • I neither see `strict_types` nor `scalar typehints` there :/ – SirPilan Aug 18 '20 at 04:06
0

This is where your little bugbear is...

if($lastDigit === $digit){

What you are expecting here is a Comparison by value and by type (===).

You can verify this using gettype();

while ($number > 0) {
    $lastDigit = $number % 10;
    echo '$lastDigit is a '.gettype($lastDigit);
    echo ' $digit is a '.gettype($digit);
    echo '<br>';

    if ($lastDigit === $digit) {
        $result++;
    }
    $number = ($number - $lastDigit) / 10;
}

Values from a POST are invariably of type String. So as $digit is a string and $lastDigit is a number (integer). Using === will fail as they are not the same type.

So as PHP has the ability to typecast on it's own... changing the === to == will work. I.E.

if ($lastDigit == $digit) {

PHP will attempt to convert the RHS value to the LHS Type and then perform the comparison.

So now you are just performing a comparison and PHP is converting $digit to an integer.

A safer way is to force the casting. So you could cast $digit from a string to an integer, and use your existing code.

function countOccurrence($number, $digit) {
    $result = 0;

    $digit = intval($digit); // Force $digit to be an integer
    while ($number > 0) {
        $lastDigit = $number % 10;
        echo '$lastDigit is a '.gettype($lastDigit);
        echo ' $digit is a '.gettype($digit);
        echo '<br>';
        if ($lastDigit === $digit) {
            $result++;
        }
        $number = ($number - $lastDigit) / 10;

        echo ' $number is a '.gettype($number);
    }

    return $result;
}

So the lesson from this, when using ===, make sure your types are also the same.

TimBrownlaw
  • 5,457
  • 3
  • 24
  • 28
  • Thank you!! I ended up just parsing the value from the $_POST like so $startNum = (int) $_POST['startNum']; Other things you mentioned are very useful as well! – Gabby Cervantes Aug 18 '20 at 03:30
  • @Tim Please consider (before posting a new answer) if a question can be resolved by referring to a pre-existing answer on Stack Overflow. Whenever this is possible, please do not answer the question -- instead close the page as a duplicate. If you have insights that are new and valuable to add to the pre-existing page, please post an answer there instead. – mickmackusa Aug 18 '20 at 03:48