andreihondrari
andreihondrari

Reputation: 5833

Incorrect filtered results with array_udiff() while performing 2-way evaluations in the callback

I've got the following PHP script:

<?php
function filt($k, $l){
    if($k===$l){
        var_dump("valid: ".$k."-".$l);
        return 0;
    }
    return 1;
}
$a6=array(7, 9, 3, 33);
$a7=array(2, 9, 3, 33);
$u=array_udiff($a6, $a7, "filt");
var_dump($u);
?>

With the following output:

string 'valid: 3-3' (length=10)

array
    0 => int 7
    1 => int 9
    3 => int 33

As I know, the array_udiff should dump the equal values and let only the different values from the first array. What seems to be the problem here? I run WampServer Version 2.2 on Windows 7. Php version: 5.3.9.

Upvotes: 1

Views: 299

Answers (3)

mickmackusa
mickmackusa

Reputation: 47991

Not performing 3-way comparison evaluations potentially disrupts the sorting process which is build into PHP's array_u*() functions.

You can see in this demo running all supported versions that there is no noticeable disruption in your specific script returning a 2-way comparison in the following versions:

  • 5.2.6 - 5.2.14,
  • 5.3.0 - 5.3.3,
  • 7.0.0 - 8.3.13

(Just because "it works" for this test case in modern versions is NOT license to program lazily/badly.)

but in these versions something "under the hood" is impacted due to the sorting of suboptimal return values.

  • 5.0.0 - 5.2.5,
  • 5.2.15 - 5.2.17,
  • 5.3.4 - 5.6.40

From PHP7.4, the most modern syntax involving the spaceship operator (3-way comparison operator) and arrow function syntax can be enjoyed. Note that because your two arrays are flat, you can simply use array_diff() to perform 3-way comparisons. Demo

$a6 = [7, 9, 3, 33];
$a7 = [2, 9, 3, 33];

var_dump(array_diff($a6, $a7));

echo "\n---\n";

var_dump(
    array_udiff($a6, $a7, fn($a, $b) => $a <=> $b)
);

Upvotes: 0

Steven Don
Steven Don

Reputation: 2431

Note that the documentation says:

The comparison function 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.

You're not doing that. To make sure that you do, simply make your filt function return $l - $k

There is a simple explanation for that: the elements might be in any order. To avoid having to compare each element to every other element, it first sorts them. That's why you need + / 0 / -

Upvotes: 4

Yoshi
Yoshi

Reputation: 54659

you're not returning all necessary values (e.g. -1, 0, 1). See: array_udiff

$a6 = array(7, 9, 3, 33);
$a7 = array(2, 9, 3, 33);

$u = array_udiff($a6, $a7, function ($k, $l){
  return $k > $l ? 1 : ($k < $l ? -1 : 0);
});

print_r($u);

Upvotes: 3

Related Questions