0

I wondered why array_uintersect_assoc()'s custom 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

When I compare two arrays, I only need a boolean return value: the elements either match or they don't.

What's the actual reason of this behavior?

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Guy Fawkes
  • 2,313
  • 2
  • 22
  • 39

2 Answers2

0

The function has been implemented this way to allow the usage of "classical" comparison functions which use such a return strategy. Such a function typically needs to be able to express three cases which is not possible with a boolean return value, for obvious reasons.

You can, however, also use a comparison function which does return a boolean result, since php as a weak typed language will automatically convert that for you. Take a look at that example which is a slightly modifed version of the one given in the function documentation:

<?php
function mystrcasecmp($a, $b) {
    return strcasecmp($a, $b) ? true : false;
}

$array1 = array("a" => "green", "b" => "brown", "c" => "blue", "red");
$array2 = array("a" => "GREEN", "B" => "brown", "yellow", "red");

print_r(array_uintersect_assoc($array1, $array2, "mystrcasecmp"));

You can see that the comparison function used here returns a boolean, yet the result is exactly the same.

Bottom line: the existing implementation is more flexible, whilst allowing for the use of a comparison function returning a boolean result too.

arkascha
  • 41,620
  • 7
  • 58
  • 90
0

I only need boolean value: the elements either match or they dont.

TL;DR

You need to invert the boolean value which will be converted to an int because these array_intersect and array_diff functions that call custom functions only qualify data that returns a zero-ish result (i.e.: null, false, "0", 0, empty string, empty array). Here is a ternary implementation:

array_uintersect_assoc($array1, $array2, fn($a, $b) => str_contains($a, $b) ? 0 : 1)

The explanation...

It's easy to get confused with this operation. Your question led me to post Unexpected results with array_uintersect_assoc() when callback returns non-numeric string.

Let's say you want to use array_uintersect_assoc() and you have these two input arrays:

$array1 = ["a" => "green", "b" => "brown", "c" => "blue", 0 => "red"];
$array2 = ["a" => "GREEN", "B" => "brown", 0 => "yellow", 1 => "red", "c" => "blue"];

Now let's say you want to make a custom function call which returns a boolean value. I'll nominate PHP8's str_contains() which will be sufficient for this demonstration. The first array will contain the haystack strings and the second array will contain the needle strings.

var_export(array_uintersect_assoc($array1, $array2, 'str_contains'));

This will check for identical keys from the first array in the second array THEN among those qualifying elements, it will check if the second array's value is found within the first array's string. Being an "intersect" call, you would intuitively expect:

['c' => 'blue']

because only the c-keyed element in the second array has a value where the second array's value case-sensitively exists in the first array's value.

However, what you actually get is:

['a' => 'green', 0 => 'red']

What?!? The reason that you get elements with keys a and 0 in the result is because any of the array_ diff/intersect functions that include u in their name are making a qualifying match when a 0 result is returned.

When the c elements' values are fed to str_contain(), a true boolean is returned. array_uintersect_assoc() then forces the boolean value to be converted to an int type. When converting booleans to ints, false becomes 0 and true becomes 1.

To fix this behavior to get the intended result, you cannot simply change the intersect word inside of the function's name to diff -- that creates:

 ['b' => 'brown', 'c' => 'blue']

This is because b doesn't have an identical corresponding key in the second array. c does have an identical corresponding key, and the true result from str_contains() is evaluated as "don't keep" by array_udiff_assoc().

Finally, the fix is to invert the boolean value so that a true becomes 0 and a false becomes a non-zero. (Demo)

var_export(
    array_udiff_assoc(
        $array1,
        $array2,
        fn($a, $b) => str_contains($a, $b) ? 0 : 1;
        // or         !str_contains($a, $b)  until the day when PHP throws a DEPRECATED warning for returning a boolean
    )
);
mickmackusa
  • 43,625
  • 12
  • 83
  • 136