我正在尝试在 PHP 中查找一个数组中不在另一个数组中的元素。 我的印象是我应该使用
array_udiff
函数和自定义定义的回调。
第一个数组是具有 id 属性的对象数组。 第二个数组是具有 name 属性的对象数组。 名称属性在组成其名称的字符串中包含 ID 号。
我的目标是确保第一个数组中每个对象的 ID 在第二个数组中都有一个对应的对象,该对象的名称中包含其 ID。 这是我的代码:
<?php
class NameObj {
public $name;
function __construct($name){
$this->name = $name;
}
}
class IdObj{
public $id;
function __construct($id){
$this->id = $id;
}
}
$idArray = array(
new IdObj(1),
new IdObj(2),
new IdObj(3)
);
$nameArray = array(
new NameObj('1 - Object 1 Name'),
new NameObj('2 - Object 2 Name')
);
function custom_diff($oId, $oName){
$splitName = explode(' - ', $oName->name);
$idFromName = $splitName[0];
$id = $oId->id;
if($idFromName == $id) return 0;
return $idFromName > $id ? 1 : -1;
}
$missing_objects = array_udiff($idArray, $nameArray, 'custom_diff');
print_r($missing_objects);
?>
我希望看到一个仅包含第一个数组中的第三个对象的数组,但我得到的是:
PHP Notice: Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice: Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice: Undefined property: NameObj::$id in /home/ubuntu/test2.php on line 36
PHP Notice: Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
PHP Notice: Undefined property: IdObj::$name in /home/ubuntu/test2.php on line 33
Array
(
[1] => IdObj Object
(
[id] => 2
)
[2] => IdObj Object
(
[id] => 3
)
)
我在这里缺少什么? 我是否错误地使用了
array_udiff()
功能?
你的使用方法不正确。具体来说,您假设始终使用第一个数组中的项目和第二个数组中的项目(按该顺序)作为参数来调用它。但 PHP 不做任何这样的保证。
现在你当然可以让你的比较检测其参数的类型并采取相应的行动,但这不是最好的主意,因为它是逆流而行。
array_udiff
应该在类似结构的数组上运行,但事实并非如此。此外,您实际上并不需要根据第二个数组的每个元素检查第一个数组的每个元素来实现您的目标。
这是我的做法(借用你的一些代码):
$extractor = function($o) { return explode(' - ', $o->name)[0]; };
$idsFromNames = array_flip(array_map($extractor, $nameArray));
foreach ($idArray as $k => $o) {
if (!isset($idsFromNames[$o->id])) {
unset($idArray[$k]);
}
}
正如 Jon 的回答中提到的,在
array_u*()
函数的回调中定义的参数与传入的数组参数不直接相关。 考虑到这些函数允许将两个以上的数组传递到函数签名中,它们怎么可能呢? 这就是许多脚本使用不明确/无意义的变量名称 $a
和 $b
的原因 - 因为无法确定它们来自哪个数组。
因此,
array_u*()
函数的回调函数必须假设每个变量可以包含来自任何提供的数组的数据。 要在这种情况下消除变量歧义并访问正确的对象属性,只需检查该变量是否是已知类之一的实例,然后访问正确的属性名称并在实现三向比较之前执行任何所需的数据准备(使用宇宙飞船操作员)。 演示
class NameObj
{
function __construct(public string $name) {}
}
class IdObj
{
function __construct(public string $id) {}
}
$idArray = [
new IdObj(1),
new IdObj(2),
new IdObj(3)
];
$nameArray = [
new NameObj('1 - Object 1 Name'),
new NameObj('2 - Object 2 Name')
];
function custom_diff($a, $b)
{
return ($a instanceof IdObj ? $a->id : strstr($a->name, ' - ', true))
<=>
($b instanceof IdObj ? $b->id : strstr($b->name, ' - ', true));
}
var_dump(array_udiff($idArray, $nameArray, 'custom_diff'));
输出:
array(1) {
[2]=>
object(IdObj)#3 (1) {
["id"]=>
string(1) "3"
}
}