如何解决array_udiff的类型强制

时间:2019-05-14 06:50:59

标签: php arrays

我想将一个复杂对象数组与一个id数组进行比较,预期结果是没有列出其id的任何对象的数组。

这听起来像是array_udiff的完美用例,但是如果没有一些令人困惑的麻烦,我将无法使其正常工作。为了说明我对该功能的问题,下面是一个简化的示例:

class Foo {
  public $id;
  public function __construct($id) {
    $this->id = $id;
  }
}

$foos = [new Foo(1), new Foo(2), new Foo(3), new Foo(4)];
$fooIds = [1, 2, 3, 4];

$diff = array_udiff($foos, $fooIds, function ($f, $i){ return $f->id - $i; });
print_r($diff);  // expected result: an empty array

// actual result:
// Array
// (
//     [1] => Foo Object
//         (
//             [id] => 2
//         )
// )
// Object of class Foo could not be converted to int :11

在我看来,array_udiff试图在数组元素之间进行某种类型的强制转换。我在文档中没有提到这一点,one question on SO似乎提出了类似的要求,但没有任何答案。我想知道的是:

  • 为什么array_udiff这样行事?如果我们可以提供任意的回调函数,则这种强制似乎是完全没有必要的,对我而言甚至是无益的。
  • 是否有解决此问题的好方法,或者鉴于我的一般问题,我应该使用其他功能吗?

3 个答案:

答案 0 :(得分:2)

尽管有点丑陋,但看似最简单的获得所需结果的方法是在比较之前进行类型检查。

<?php
declare(strict_types=1);

error_reporting(-1);
ini_set('display_errors', 'On');

class Foo
{
    public $id;

    public function __construct($id) {
        $this->id = $id;
    }
}

$foos = [new Foo(1), new Foo(2), new Foo(3), new Foo(4)];
$fooIds = [1, 2, 3, 4];

$diff = array_udiff($foos, $fooIds, function ($a, $b) {
    echo gettype($a), ' <=> ', gettype($b), "\n";

    if ($a instanceof Foo && $b instanceof Foo) {
        return $a->id <=> $b->id;
    }

    if ($a instanceof Foo) {
        return $a->id <=> $b;
    }

    if ($b instanceof Foo) {
        return $a <=> $b->id;
    }

    return $a <=> $b;
});

print_r($diff);  // expected result: an empty array

演示:https://3v4l.org/1uVYf

答案 1 :(得分:2)

我认为@apokryfos(来自the answer you shared):

  

在第一个数组和$ b中不会有$a   从第二个数组。您实际上在功能中拥有的是   比较器,因此,如果您基于比较器对两个数组进行排序(在O(nlogn)时间内,则可以通过排序联接在O(n)时间内获得diff)   方式。如果只是成对比较,它将是O(n^2),所以我   建议您将回调作为一般的比较器函数   适用于组合的第一和第二个数组

无论如何,我建议使用array_column和星际飞船运营商一种简单的解决方法

$diff = array_udiff(array_column($foos, "id"), $fooIds, function ($f, $i){ return $f <=> $i; });

array_column也适用于PHP 7中的对象

答案 2 :(得分:2)

使用专门用于获取Foo$f->id)或非Foo$f)的比较值的中间函数:

$val = function($f) { 
    return $f instanceof Foo ? $f->id : $f;
};

$diff = array_udiff(
    $foos,
    $fooIds, 
    function ($f, $i) use ($val) { return $val($f) - $val($i); }
);

或在单个比较功能中:

function ($f, $i) { return ($f instanceof Foo ? $f->id : $f) - ($i instanceof Foo ? $i->id ? $i); }