2

I'm trying to compute difference of two arrays of objects with array_udiff(). My object structure is complicated and I can not rely on quantitative properties, because those properties of objects in both arrays may have same values, but they are stored as different instances (it is expected behavior).

So, here is my question: is there any way to detect same instances in both arrays using reference detection?

What have I tried?

I've tried this:

<?php
header('Content-Type: text/plain; charset=utf-8');

$a = new stdClass;
$b = new stdClass;
$c = new stdClass;

$a->a = 123;
$b->b = 456;
$c->c = 789;

$x = [ $a, $c ];
$y = [ $b, $c ];

$func   = function(stdClass &$a, stdClass &$b){
    $replacer   = time();
    $buffer     = $a;

    $a = $replacer;

    $result = $a === $b;

    $a = $buffer;

    return $result;
};

$diff   = array_udiff($x, $y, $func);

print_r($diff);
?>

And got unsuccessful results, because if I try to replace value for $x element, php will not remove reference from $y.

I have same output for:

$func   = function(stdClass &$a, stdClass &$b){
    return $a === $b;
};

and for

$func   = function(stdClass &$a, stdClass &$b){
    return $a == $b;
};

It is an empty array.

Any suggestions?

4

3 回答 3

1

这是您的问题,来自手册页:

如果认为第一个参数分别小于、等于或大于第二个参数,则比较函数必须返回一个小于、等于或大于零的整数。

您正在返回一个布尔值 ( false),因为您正在将时间戳与一个对象进行比较。(int) false为零,因此每个对象都将被视为相等,因此您的$diffArray 为空。
将您的回调函数更改为:

$func = function(stdClass $a, stdClass $b)
{//don't pass by reference, it's quite dangerous
    $replacer = time();
    $buffer = $a;
    $a = $replacer;
    $result = $a === $b ? 0 : -1;//equal? return zero!
    $a = $buffer;
    return $result;
};

这会起作用,但$diff 现在显然会包含所有对象。而且您的回调有点混乱,请考虑:

$func = function(stdClass $a, stdClass $b)
{//don't pass by reference, it's quite dangerous
    return $a === $b ? 0 : -1;//compare objects
    //or, if you want to compare to time, still:
    return time() === $b ? 0 : -1;
};

这是一个更清洁,更短的地狱,不是吗?

注意如果两个对象不相等,
您将不得不返回。-1在这种情况下返回1意味着您正在比较的对象已经大于您正在比较的值。在这种情况下,PHP 将停止查找,并简单地假设该值不存在于您将第一个数组与之比较的数组中......好吧,这变得相当复杂。举个例子:

[$a, $c] compare to [$b, $c]:
$a === $b => false: returns 1
    PHP assumes $a > $b, so $a > $c is implied, $a is pushed into $diff
$c === $b => false returns 1
   PHP assumes $c > $b, and is pushed to $diff, it's never compared to the next elem...

返回 -1 为 false,但是:

[$a, $c] compare to [$b, $c]:
$a === $b => false: returns -1
    PHP assumes $a < $b, so:
$a === $c => false: returns -1
    No more elems left, push $a to $diff
$c === $b => false returns -1
   PHP assumes $c < $b, moving on:
$c === $c => true returns 0
   Match found, $c is NOT pushed to $diff
于 2013-10-15T13:04:10.957 回答
0

尽管我已经接受了@ Elias Van Ootegem 的回答,但它对 diff 数组中任何可能的对象组合都无济于事。

要检测引用,您可以使用===!==比较运算符

当使用恒等运算符(===) 时,对象变量是相同的当且仅当它们引用同一个类的同一个实例

所以这里是函数,它接受两个数组,但可能会改进以接受更多:

function array_exclude_instances(array $source, array $excludes){
    foreach($source as $index => $current){
        foreach($excludes as $exclude){
            if($exclude !== $current)continue 1;

            unset($source[$index]);

            continue 2;
        }
    }

    return array_values($source);
}

测试:

$a = new stdClass;
$b = new stdClass;
$c = new stdClass;

$a->x = 123;
$b->x = 123;
$c->x = 456;

$x = [$a, $c];
$y = [$c, $b];

$result = array_exclude_instances($x, $y);

结果:

Array
(
    [0] => stdClass Object
        (
            [x] => 123
        )

)

在线测试@ 3v4l

于 2013-10-17T10:21:06.200 回答
-1

如果你更换会发生什么:

$func   = function(stdClass &$a, stdClass &$b)

至:

function func(stdClass &$a, stdClass &$b)

并像这样调用:

$diff   = array_udiff($x, $y, 'func');
于 2013-10-15T12:59:56.880 回答