PHP中用于对象数组交集的函数

16

我想知道如何对对象数组使用array_intersect。


“Object array”是什么意思 - 你的意思是对象数组吗? - thetaiko
是的,那是对象数组。 - zahir hussain
8个回答

19

您可以将 array_uintersect 与 spl_object_hash 结合使用,以下是示例:

    array_uintersect($a, $b, function($a, $b) {
        return strcmp(spl_object_hash($a), spl_object_hash($b));
    });

其中'$a'和'$b'是一些你想要求交集的对象数组。


4
不错的解决方案。针对现代读者:在php7中,可以用<=>运算符代替strcmp(),代码如下:return spl_object_hash($a) <=> spl_object_hash($b); - d-ph
寻找一个好的解决方案。如果你想要引用类中的方法,你也可以使用array_uintersect($a, $b, array($this, 'your_class_method'))。 - Thanasis

8

已经实现了优秀的toString函数,被称为serialize ;) 所以

array_map(
    'unserialize',
    array_intersect(
        array_map(
            'serialize',
            $obj1
        ), 
        array_map(
            'serialize', 
            $obj2
        )
    )
);

会做这个工作,之前提到的示例不起作用,因为array_intersect只能与字符串一起使用,正如有人也提到的那样。


经过进一步的思考,如果对象具有方法,则这并不足够。当您序列化时,会丢失方法,因此只保留属性。 - yitwail

2

几天前我遇到了类似的问题,尽管这些答案都有正确的方向;但是我使用它们来得出以下结论:

从Artefacto的答案中,return $obj1 == $obj2 并没有真正起作用,所以我编写了一个简单的比较函数(基本上是获取序列化对象的md5并进行比较):

function object_compare($obj1, $obj2){
  $md5 = function($obj){
    return md5(serialize($obj));
  };
  return strcmp($md5($obj1), $md5($obj2));
}

然后只需要使用我们的比较函数调用array_uintersect即可获得交集:
# $array1 / $array2 are the array of objects we want to compare
return array_uintersect($array1, $array2, 'object_compare');

在我的情况下,我有一个未知/动态对象数组,所以我采取了更进一步的措施,这样我就不必专门声明array_uintersect($array1, $array2, ...),只需能够传入一个数组 (由对象组成) 的数组:

# $multiarray_of_objects is our array of arrays
$multiarray_of_objects[] = 'object_compare';
return call_user_func_array('array_uintersect', $multiarray_of_objects);

只需记得将我们的回调/比较函数的引用作为数组中的最后一个字符串传递。像魔法一样起作用!


由于我是在控制器类中工作,所以我不能完全像这样做,但使用您的方法和 Artefacto 的格式非常成功。 - Magnanimity
在一个类中,return array_uintersect($array1, $array2, 'self::object_compare'); 应该可以工作,甚至可以使用 ClassName::object_compare,但请记住它需要是一个公共的类函数。 - Atari

2

4
不准确。对象可以有至少三种转换成字符串的方式。它们可能是PHP类并实现__toString,它们可能有一个接受IS_STRING的强制处理程序,或者它们可能有一个获取处理程序,返回可转换为字符串的zval。 - Artefacto
+1 @Artefacto,请提交一个使用任意对象和array_intersect()函数的示例答案。 - Dolph

1

检查两个对象是否相等的正确方法是使用==。 因此:

array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 == $a2; });

1
你需要更具体一些。哪里出了问题?有没有错误信息?是什么错误信息?输出结果不是预期的吗?预期是什么?你得到了什么? - Artefacto
1
这个不起作用是因为array_unintersect需要比较函数返回1、-1或0。请参阅此处的完整说明和示例http://php.net/manual/en/function.array-uintersect.php#72841。 - Tim

1
仅为完整性考虑:在对象中实现__toString()方法,返回一个唯一的值。对于数据库实体来说,这可能很容易,只需返回记录ID后缀的完全限定类名即可。但是,也可以通过进行一些哈希或更糟糕的操作来任意复杂化。
我认为,序列化自身或创建某些唯一比较其对象的内容是类的职责。使用类外部的任何东西来序列化对象可能会导致奇怪的行为(包括比较不同类的对象,这绝不能导致相等)。

0

我使用array_udiff来实现对象数组的array_intersect。

 function diff($a, $b) {
 if($a === $b) {
     return 0;
 } else {
     return 1;}
 }

 $array_1 = array('a', 'b', 'c');    

 $array_2 = array('c', 'd','e');    

 $array = array_udiff($array_1, array_udiff($array_1, $array_2, 'diff'),'diff');

var_dump($array);
return array(1) { [2]=> string(1) "c" }

你可以为任何方案编写自己的差异函数。


-2

正确的解决方案应该是:

array_uintersect($arr1, $arr2, function ($a1, $a2) { return $a1 != $a2; });

请注意回调函数中的!=,与@Artefacto的答案不同。根据array_uintersect的文档,如果数组项相等,则回调函数必须返回0(false)。

这是一种不必要的类型破解,回调函数应该返回整数(-1、0、1),如果第一个值小于、等于或大于第二个值,就像排序函数一样。将false隐式类型转换为0会使您的代码读者感到困惑。 - amik

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接