我正在尝试使用array_udiff()
计算两个对象数组的差异。我的对象结构很复杂,我不能依赖于定量属性,因为两个数组中对象的那些属性可能具有相同的值,但它们存储为不同的实例(它是预期的行为)。
所以,这是我的 问题 :是否有办法使用参考检测检测两个阵列中的相同实例?
我尝试了什么?
我试过这个:
<?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);
?>
结果不成功,因为如果我尝试替换$x
元素的值,php将不会删除$y
的引用。
我有相同的输出:
$func = function(stdClass &$a, stdClass &$b){
return $a === $b;
};
和
$func = function(stdClass &$a, stdClass &$b){
return $a == $b;
};
这是一个空数组。
有什么建议吗?
答案 0 :(得分:1)
这是您的问题,请参见手册页:
如果第一个参数被认为分别小于,等于或大于第二个参数,则比较函数必须返回小于,等于或大于零的整数。
您正在返回布尔值(false
),因为您正在将时间戳与对象进行比较。 (int) false
为零,因此每个对象都会被视为相等,因此$diff
数组为空。
将您的回调函数更改为:
$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:
[$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
答案 1 :(得分:0)
如果替换会发生什么:
$func = function(stdClass &$a, stdClass &$b)
到:
function func(stdClass &$a, stdClass &$b)
并且这样打电话:
$diff = array_udiff($x, $y, 'func');
答案 2 :(得分:0)
尽管我已经接受了@ Elias Van Ootegem 的答案,但它对于diff阵列中任何可能的对象组合都无济于事。
要检测引用,您可以使用===
or !==
comparison operators:
使用 身份运算符 (===)时,对象变量是相同的 当且仅当他们引用同一 类 的 同一实例 。
所以这是函数,它接受两个数组,但可能会被改进以接受更多:
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。