对象数组的数组交集
Array intersection for array of objects
我有一个对象数组数组,我正在尝试获取在所有内部数组中找到的对象的交集。我的第一次尝试是在外部数组上使用扩展运算符并将其传递给 array_intersect
:
array_intersect(...array_values($test))
但是,函数 array_intersect
使用基于字符串的比较 string representation:
Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same.
我可以轻松地在我的大多数对象上实现 __toString
,但是有人已经将它用于其他目的,并且不能保证它是唯一的。如果我必须硬着头皮重构,我会的,但我只是看看是否有另一种方法可以彻底解决这个问题。
数组中的键无关紧要,外数组和内数组都是无界的。
(如果有人想知道,这是一堆组件的汇总搜索结果,如果有人想知道的话,它们会被放在一起)。
基本要点是这样的:
class sample
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
// Comment this function out to see the error
public function __toString()
{
return spl_object_hash($this);
}
}
$obj1 = new sample('1');
$obj2 = new sample('2');
$obj3 = new sample('3');
$obj4 = new sample('4');
$obj5 = new sample('5');
$test = [
'a' => [
$obj1,
$obj2,
$obj5,
],
'b' => [
$obj2,
$obj3,
$obj5,
],
'c' => [
$obj2,
$obj3,
$obj5,
],
'd' => [
$obj2,
$obj4,
$obj5,
],
];
print_r(array_intersect(...array_values($test)));
运行 将产生我想要的结果:
Array
(
[1] => sample Object
(
[id] => 2
)
[2] => sample Object
(
[id] => 5
)
)
但是,如果您注释掉 __toString()
方法,您将看到异常 Object of class sample could not be converted to string
好吧,我写了这么久,以至于我的大脑能够转换思维,我自己想出了一个答案。不是很优雅,但仍然很干净,我不需要重构。
$final = array_shift($test);
while (count($test)) {
$to_test = array_shift($test);
$final = array_uintersect($final, $to_test, static function ($a, $b) {
return spl_object_hash($a) <=> spl_object_hash($b);
});
}
这只使用 array_uintersect
允许自定义比较函数,我们一次遍历每个数组并计算交集
嗯,你也可以实现自己的比较函数。
看看。
<?php declare(strict_types=1);
final class SampleComparator
{
public static function compare(array $samples): array
{
if (!$firstElement = array_shift($samples)) {
// If array is empty
return [];
}
return self::compareWithSampleElements($firstElement, $samples);
}
private static function compareWithSampleElements(
array $headSamples,
array $tailListSamples
): array {
$result = [];
/** @var Sample $headSample */
foreach ($headSamples as $headSample) {
$result = self::iterateHeadSamples($tailListSamples, $headSample, $result);
}
return $result;
}
private static function iterateHeadSamples(
array $tailListSamples,
Sample $headSample,
array $result
): array {
/** @var array $tailSamples */
foreach ($tailListSamples as $tailSamples) {
$result = self::iterateTailSamples($tailSamples, $headSample, $result);
}
return $result;
}
private static function iterateTailSamples(
array $tailSamples,
Sample $headSample,
array $result
): array {
foreach ($tailSamples as $tailSample) {
if ($headSample === $tailSample) {
$result[$headSample->id] = $headSample;
continue;
}
}
return $result;
}
}
用法:
$intersection = SampleComparator::compare($test);
print_r($intersection);
----------
*OUTPUT*
Array
(
[2] => Sample Object
(
[id] => 2
)
[5] => Sample Object
(
[id] => 5
)
)
当然还有测试。
<?php declare(strict_types=1);
final class SampleComparatorTest extends TestCase
{
/**
* @test
* @dataProvider sampleProvider
*/
public function sampleComparator(array $expected, array $actual): void
{
self::assertSame($expected, SampleComparator::compare($actual));
}
public function sampleProvider(): array
{
$obj1 = new Sample('1');
$obj2 = new Sample('2');
$obj3 = new Sample('3');
$obj4 = new Sample('4');
$obj5 = new Sample('5');
return [
'no values at all' => [
[],
[]
],
'no tail values' => [
[],
[
[$obj1, $obj2]
]
],
'no same values' => [
[],
[
[$obj1, $obj2],
[$obj3, $obj4, $obj5]
]
],
'repeat values' => [
[1 => $obj1],
[
[$obj1, $obj1],
[$obj1, $obj1, $obj1]
]
],
'only 2 and 5' => [
[2 => $obj2, 5 => $obj5],
[
[$obj1, $obj2, $obj5],
[$obj2, $obj3, $obj5],
[$obj2, $obj3, $obj4],
]
],
];
}
}
我有一个对象数组数组,我正在尝试获取在所有内部数组中找到的对象的交集。我的第一次尝试是在外部数组上使用扩展运算符并将其传递给 array_intersect
:
array_intersect(...array_values($test))
但是,函数 array_intersect
使用基于字符串的比较 string representation:
Two elements are considered equal if and only if (string) $elem1 === (string) $elem2. In words: when the string representation is the same.
我可以轻松地在我的大多数对象上实现 __toString
,但是有人已经将它用于其他目的,并且不能保证它是唯一的。如果我必须硬着头皮重构,我会的,但我只是看看是否有另一种方法可以彻底解决这个问题。
数组中的键无关紧要,外数组和内数组都是无界的。
(如果有人想知道,这是一堆组件的汇总搜索结果,如果有人想知道的话,它们会被放在一起)。
基本要点是这样的:
class sample
{
public $id;
public function __construct($id)
{
$this->id = $id;
}
// Comment this function out to see the error
public function __toString()
{
return spl_object_hash($this);
}
}
$obj1 = new sample('1');
$obj2 = new sample('2');
$obj3 = new sample('3');
$obj4 = new sample('4');
$obj5 = new sample('5');
$test = [
'a' => [
$obj1,
$obj2,
$obj5,
],
'b' => [
$obj2,
$obj3,
$obj5,
],
'c' => [
$obj2,
$obj3,
$obj5,
],
'd' => [
$obj2,
$obj4,
$obj5,
],
];
print_r(array_intersect(...array_values($test)));
运行 将产生我想要的结果:
Array
(
[1] => sample Object
(
[id] => 2
)
[2] => sample Object
(
[id] => 5
)
)
但是,如果您注释掉 __toString()
方法,您将看到异常 Object of class sample could not be converted to string
好吧,我写了这么久,以至于我的大脑能够转换思维,我自己想出了一个答案。不是很优雅,但仍然很干净,我不需要重构。
$final = array_shift($test);
while (count($test)) {
$to_test = array_shift($test);
$final = array_uintersect($final, $to_test, static function ($a, $b) {
return spl_object_hash($a) <=> spl_object_hash($b);
});
}
这只使用 array_uintersect
允许自定义比较函数,我们一次遍历每个数组并计算交集
嗯,你也可以实现自己的比较函数。
看看。
<?php declare(strict_types=1);
final class SampleComparator
{
public static function compare(array $samples): array
{
if (!$firstElement = array_shift($samples)) {
// If array is empty
return [];
}
return self::compareWithSampleElements($firstElement, $samples);
}
private static function compareWithSampleElements(
array $headSamples,
array $tailListSamples
): array {
$result = [];
/** @var Sample $headSample */
foreach ($headSamples as $headSample) {
$result = self::iterateHeadSamples($tailListSamples, $headSample, $result);
}
return $result;
}
private static function iterateHeadSamples(
array $tailListSamples,
Sample $headSample,
array $result
): array {
/** @var array $tailSamples */
foreach ($tailListSamples as $tailSamples) {
$result = self::iterateTailSamples($tailSamples, $headSample, $result);
}
return $result;
}
private static function iterateTailSamples(
array $tailSamples,
Sample $headSample,
array $result
): array {
foreach ($tailSamples as $tailSample) {
if ($headSample === $tailSample) {
$result[$headSample->id] = $headSample;
continue;
}
}
return $result;
}
}
用法:
$intersection = SampleComparator::compare($test);
print_r($intersection);
----------
*OUTPUT*
Array
(
[2] => Sample Object
(
[id] => 2
)
[5] => Sample Object
(
[id] => 5
)
)
当然还有测试。
<?php declare(strict_types=1);
final class SampleComparatorTest extends TestCase
{
/**
* @test
* @dataProvider sampleProvider
*/
public function sampleComparator(array $expected, array $actual): void
{
self::assertSame($expected, SampleComparator::compare($actual));
}
public function sampleProvider(): array
{
$obj1 = new Sample('1');
$obj2 = new Sample('2');
$obj3 = new Sample('3');
$obj4 = new Sample('4');
$obj5 = new Sample('5');
return [
'no values at all' => [
[],
[]
],
'no tail values' => [
[],
[
[$obj1, $obj2]
]
],
'no same values' => [
[],
[
[$obj1, $obj2],
[$obj3, $obj4, $obj5]
]
],
'repeat values' => [
[1 => $obj1],
[
[$obj1, $obj1],
[$obj1, $obj1, $obj1]
]
],
'only 2 and 5' => [
[2 => $obj2, 5 => $obj5],
[
[$obj1, $obj2, $obj5],
[$obj2, $obj3, $obj5],
[$obj2, $obj3, $obj4],
]
],
];
}
}