【问题标题】:Compare two arrays of objects by references?通过引用比较两个对象数组?
【发布时间】:2013-10-23 06:42:07
【问题描述】:

我正在尝试使用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;
};

这是一个空数组。

有什么建议吗?

【问题讨论】:

  • $buffer = $a; ... $a = $buffer; ==> $a 并没有真正改变,是吗?确定它被分配了$replacer,但随后它被重新分配回原来的值......并且$diff将只包含false
  • @EliasVanOotegem $diff 实际上包含一个空数组。检查。另外,为什么=== 不提供true
  • 因为$bstdClass 的一个实例,并且在重新分配该引用后将其与$a 进行比较 int 对象!== ints
  • @EliasVanOotegem 这个东西工作过一次here,所以我刚刚检查过了。
  • 当然它在那里工作,但该代码是比较原语,而不是对象......有很大的不同。顺便说一句:我在回答中说过,不要使用&amp; 运算符(无论如何都不需要对象)here's why,你可能想通读一遍

标签: php arrays reference array-difference


【解决方案1】:

如果替换会发生什么:

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

到:

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

并像这样调用:

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

【讨论】:

  • 相同的输出。没有什么变化。另外,我相信没有区别。不是吗?
【解决方案2】:

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

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

您返回的是布尔值 (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...

在 false 时返回 -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

【讨论】:

  • codepad.viper-7.com/E60h70 ?另外,为什么它不从您的答案中排除 $c 实例?
  • @HAL9000:因为 $x 和 $y both 都将 $c 作为其值之一。 array_udiff 迭代第一个数组,并将 每个 值与 每个其他数组中的每个值 进行比较任何其他数组,都不会返回,如果是,则 _.在您的情况下,$a$c 分别针对 [ $b, $c ],找到了 $c,因此不包括在内,未找到 $a,因此 $diff 将包含 $a
  • @HAL9000:也更新了我的答案...忘记了小于/大于区别...
【解决方案3】:

尽管我已经接受了@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.

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-05
    • 2023-03-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多