5

I need to compare database values with post values. If post values (decimal price) are within 2 cents threshold consider values equal. Result is array with 'real' difference. Arrays are consistent: same number of values, same keys.

$db_values =   array( "21" => 10.00, "22" => 20.00, "25" => 3.55);
$post_values = array( "21" => 9.98,  "22" => 20.01, "25" => 2.55 ); 

I'm trying to compare Absolute value to my tolerance value -- epsilon (Compare decimals in PHP) and array_udiff:

function epsilon_compare ($v1,$v2)
{
 $epsilon = 0.02;
 $diff = abs($v1 - $v2);

   if ($diff <= $epsilon) 
    { 
            return 0;
      //echo "numbers are equal";
      } else {
         return 1;
             }
    }

 print_r(array_udiff($post_values, $db_values, "epsilon_compare"));

gives correct result: Array ( [25] => 2.55 )

but when i use different array I get wrong result eg:

   $db_values =   array( "21" => 10.00, "22" => 20.00, "25" => 3.55);
   $post_values = array( "21" => 8.00,  "22" => 20.01, "25" => 2.55 );

In this case it gives:

   Array ( [21] => 8 [22] => 20.01 [25] => 2.55 ) 

Key [22] => 20.01 is listed but it is within the threshold so it shouldn't be in result set. I think I don't fully understand array_udiff. thanks.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
phpJs
  • 432
  • 1
  • 4
  • 23
  • what happens if you reverse the order of the arrays passed in to array_udif? (i.e., call it with ($db_values, $post_values, "epsilon_compare")). If you get different results, it might suggest something funny is going on with "epsilon_compare". – imm Oct 10 '11 at 04:24
  • for a start the compare fn should not return boolean. "integer less than, equal to, or greater than zero". – poop-deck Oct 10 '11 at 06:41

2 Answers2

1

I don't think that udiff does what you think it does. If you print v1 and v2 it will give you the following:

v1: 20.01 v2: 9.98
v1: 2.55 v2: 20.01
v1: 20 v2: 10
v1: 3.55 v2: 20
v1: 9.98 v2: 10
v1: 9.98 v2: 20.01
v1: 20.01 v2: 20
v1: 20.01 v2: 2.55
v1: 2.55 v2: 3.55 

It makes more comparisons than you think.

You probably need a piece of code that looks something like:

function compare_arrays($array1, $array2){
    $result = array();
    foreach($array1 as $value => $outcome){
        if(epsilon_compare($array1[$value], $array2[$value])){
            $result[$value] = $array2[$value];
        }
    }
    return $result;
}
Dennis
  • 138
  • 1
  • 4
  • when using my "epsilon_compare" function it gives errors (undefined offset). – phpJs Oct 10 '11 at 17:52
  • when testing VolkerK solution i get inconsistent results. this is probably way to do it if you can help me out to make it work.. – phpJs Oct 10 '11 at 19:45
  • I have corrected the results for associative arrays. If you have multiple occuring associations in your arrays, add the following: if(isset($result[$value])) array_push($result[$value],$array2[$value]); – Dennis Oct 11 '11 at 17:05
  • This is the correct answer, VolkerK's answer should not be used. `array_udiff()` is NOT respecting the keys which relate the two array elements. `array_udiff_assoc()` will issue a DEPRECATED notice from the custom callback function because of the boolean return value. There is no benefit from employing a `_diff` function for this task. This answer is 100% how I would do it. – mickmackusa Sep 01 '22 at 03:33
-1

http://docs.php.net/array_udiff:

The user supplied callback function is used for comparison. It must return an integer less than, equal to, or greater than zero if the first argument is considered to be respectively less than, equal to, or greater than the second.
Your function always returns 1 if the elements are unequal which results in diffrent (wrong) comparisons decisions.
<?php
function epsilon_compare ($v1,$v2)
{
    $epsilon = 0.02;
    $diff = $v1 - $v2;
    return abs($diff)<=$epsilon ? 0 : $diff;
}

$db_values =   array( "21" => 10.00, "22" => 20.00, "25" => 3.55);
$post_values = array( "21" => 8.00,  "22" => 20.01, "25" => 2.55 );
print_r(array_udiff($post_values, $db_values, "epsilon_compare"));

prints

Array
(
    [21] => 8
    [25] => 2.55
)

edit: using array_udiff_assoc

$db_values = array ( "1" => 7.55, "2" => 5.45, "3" => 5.45, "4" => 64.45, "5" => 54.75, "6" => 30.40, "7" => 56.99, "8" => 10.90, "9" => 60.85, "11" => 3.25, "12" => 13.05, "13" => 5.45, "14" => 8.00, "15" => 5.45, "16" => 13.05, "17" => 4.35 );
$post_values = array ( "1" => 7.55, "2" => 5.45, "3" => 5.45, "4" => 64.45, "5" => 54.75, "6" => 30.40, "7" => 56.99, "8" => 10.90, "9" => 60.85, "11" => 3.25, "12" => 13.05, "13" => 2.45, "14" => 8.00, "15" => 5.45, "16" => 12.05, "17" => 2.34 );
print_r(array_udiff_assoc($post_values, $db_values, "epsilon_compare"));

prints

Array
(
    [13] => 2.45
    [16] => 12.05
    [17] => 2.34
)
VolkerK
  • 95,432
  • 20
  • 163
  • 226
  • what would you say about Dennis comment that "udiff" makes more comparisons that is in this case needed? – phpJs Oct 10 '11 at 17:53
  • `$db_values = array ( "1" => 7.55, "2" => 5.45, "3" => 5.45, "4" => 64.45, "5" => 54.75, "6" => 30.40, "7" => 56.99, "8" => 10.90, "9" => 60.85, "11" => 3.25, "12" => 13.05, "13" => 5.45, "14" => 8.00, "15" => 5.45, "16" => 13.05, "17" => 4.35 );` `$post_values = array ( "1" => 7.55, "2" => 5.45, "3" => 5.45, "4" => 64.45, "5" => 54.75, "6" => 30.40, "7" => 56.99, "8" => 10.90, "9" => 60.85, "11" => 3.25, "12" => 13.05, "13" => 2.45, "14" => 8.00, "15" => 5.45, "16" => 12.05, "17" => 2.34 );` If you use above two arrays, the result is incorrect (only 16=>12.05). – phpJs Oct 10 '11 at 19:39
  • That's how array_udiff works. `Returns an array containing all the values of array1 that are not present in any of the other arguments.` For other results you have to specify/explain the rules... – VolkerK Oct 11 '11 at 05:25
  • I need to compare array value by the key. so value of key "17" from first array against value of key "17" in second array and not any other values. perhaps foreach loop will be better in this case. – phpJs Oct 11 '11 at 05:48
  • so far I've been testing `udiff_assoc` works fine. I changed this line: ` return abs($diff)<=$epsilon ? 0 : 1; ` from $diff to 1. should be ok(?) – phpJs Oct 11 '11 at 23:57
  • `array_udiff()` is NOT respecting the keys which relate the two array elements. `array_udiff_assoc()` will issue a DEPRECATED notice from the custom callback function because of the boolean return value. There is no benefit from employing a `_diff` function for this task. A basic loop which simultaneously compares each related element is the correct solution. – mickmackusa Sep 01 '22 at 03:34