0

I get very strange PHP behavior with my function that allow to search within array of associative arrays to find array that have some key with searchable value.

My PHP code:

<?php

// Test array - results from database

$test_array[0] = array('period' => '2018', 'payment_type' => 'Direct Debit', 'value' => 0);
$test_array[1] = array('period' => '2018', 'payment_type' => 'Manual', 'value' => 20.85);
$test_array[2] = array('period' => '2018', 'payment_type' => 'Credit Debit Card', 'value' => 0);

// Function to find subarrays by payment type
function searchReportArrayForKeyValue($array, $searchvalue) {

    $result_array = Array();

    for($i = 0; $i < count($array); ++$i) {
        $array_inside = $array[$i];
        $keys = array_keys($array_inside);

        for($j = 0; $j < count($array_inside); ++$j) {
            $value = $array_inside[$keys[$j]];

            if($value == $searchvalue) {
                $result_array = $array_inside;
            }

        }
    }

    return $result_array;
}

var_dump(searchReportArrayForKeyValue($test_array, 'Direct Debit'));
var_dump(searchReportArrayForKeyValue($test_array, 'Manual'));
var_dump(searchReportArrayForKeyValue($test_array, 'Credit Debit Card'));

?>

If I run this code I should get 3 different arrays returned (0, 1, 2 keys from test array), however all three functions calls return 'Credit Debit Card' array key: http://take.ms/hZfec (screenshot). BUT if I change 'value' => 0, to some float/integer all works as expected, for example if I change test array to this:

$test_array[0] = array('period' => '2018', 'payment_type' => 'Direct Debit', 'value' => 11.2);
$test_array[1] = array('period' => '2018', 'payment_type' => 'Manual', 'value' => 20.85);
$test_array[2] = array('period' => '2018', 'payment_type' => 'Credit Debit Card', 'value' => 10.5);

I get 3 correct different subarrays returned by my three function calls: http://take.ms/SSTu1 (screenshot)

Why this happens? How this ZERO in 'value' break arrays iteration? What is wrong in my function?

P.S.: previously I have 'for each' in code, and changed it to 'for' to make sure this issue does not related to pointer reset in 'for each' inside 'for each', but this does not helped in this situation.

3
  • 2
    Did you try === in if($value === $searchvalue) { Commented Jan 23, 2019 at 16:45
  • Is there a good reason not to do this in the database? Why would you want to hold the entire table result in PHP memory mocking the WHERE clause there? Commented Jan 23, 2019 at 17:00
  • What is your actual intention? (XY problem) Commented Jan 23, 2019 at 17:08

3 Answers 3

1

Finally I fixed this with this code changes:

I changed this:

if($value == $searchvalue)

To this:

if(strval($value) == strval($searchvalue))

But I'am still does not understand how and why this gives so strange behavior.

Sign up to request clarification or add additional context in comments.

2 Comments

if you compare any string with an integer, the string is converted to int. If the string is not a number, result is 0 (e.g. "abc" == 0 is equal to 0 ==0). In your first example, the value is 0...
You should just use "===" instead "=="
1

You are using a loose comparison using ==

In every loop, the last comparision is 0 == Direct Debit which is true, and then you set $result_array = $array_inside;

You could see it when you run for example:

echo "$value == $searchvalue = " . ($value == $searchvalue ? "true" : "false") . PHP_EOL;
if($value == $searchvalue) {

Comments

1

You can't compare floating point values directly because not all rational decimal numbers have rational floating-point equivalents.

See: https://en.wikipedia.org/wiki/Floating-point_arithmetic#Accuracy_problems

To properly compare floating point numbers for you need to do something like:

$e = 0.00001;

if( abs($float_a - $float_b) < $e );

Where $e is a sufficiently small margin of error for a particular comparison.

Your solution of casting to string only works because PHP's default float format precision is 14 digits and the imprecision is less than that.

That said, no one should ever use floating point to record amounts of money for these exact reasons, and more. Generally you store the value as an integer of the base unit of currency, eg: $1 == 100 cents, 1BTC == 1,000,000 satoshis.

However there are further concerns, such as safely splitting $4 across 3 recipients, safely rounding amounts without losing pennies, etc. For this reason there is Fowler's Money Pattern, and libraries that implement it.

Comments

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.