检查两个数组是否具有相同的值(不考虑值的顺序)

50
[2,5,3]    

[5,2,3]

它们是相等的,因为它们具有相同的值,但不是相同的顺序。

我可以不使用foreach()in_array()循环来找出吗? 我认为这样效率不高。


1
array_diff() 不是完美的函数。先排序再比较是更好的选择。 - Suresh Kamrushi
1
@jeroen:运行以下代码:$array = array(4,3); $array2 = array(2,3,4); echo var_dump(array_diff($array,$array2)); - Suresh Kamrushi
@SureshKamrushi 不错! - jeroen
所以array_diff不起作用? - Anna K.
1
截至我撰写此文时,所提供的解决方案均无法正常工作。它们全部都以布尔值失败。 - Jay Bienvenu
13个回答

89
sort($a);
sort($b);
if ($a===$b) {//equal}

1
排序函数具有对数据进行排序的副作用,因此如果您想保留原始顺序,在使用之前需要将其复制到 $a 和 $b 中。 - Ray
18
我不能接受这个答案。对于 ['a'][true] 都返回 true。 - Jay Bienvenu
2
需要注意的是,测试 sort($a) == sort($b) 总是返回 true,我猜测这是因为 PHP 检查 sort() 的返回值可能是 1,而不是查看数组。 - siliconrockstar
2
这不是最佳答案,排序会影响数组的值。它并不适用于所有情况。 - Ali Akbar Azizi
4
@JayBienvenu 用===代替==测试了这个案例,['a'][true]返回false。请注意不改变原意,使句子通俗易懂。 - Cameron Wilby
1
如果是 ===,我会点赞的,但是没有那个额外的 =,这就不是最正确的答案了。 - Chris

30

我来晚了。我有同样的问题,但不想进行排序,这是我知道会起作用的直接答案。我想出了这个简单的单行代码,但它只适用于唯一值的数组

$same = ( count( $a ) == count( $b ) && !array_diff( $a, $b ) )

它也比排序选项快约5倍。 两者都不是特别慢,所以我认为更多关于您个人偏好,以及哪个选项更清晰。就我个人而言,我宁愿不排序。

编辑:感谢Ray指出这只适用于具有唯一值的数组。


1
我认为这是最好的答案,被接受的答案会留下一个副作用,即实际上对你可能不想要排序的数组$a和$b进行排序。 - Ray
3
深入了解后发现,array_diff 并不总是有效,因为它会将多个结果合并成一个。以下方式看起来可以实现:$a = ['red','red','blue','blue']; $b = ['red','red','red','blue']; - Ray
1
对于 [true][1],返回 true - Jay Bienvenu
2
确实,@JayBienvenu许多PHP函数对类型并不严格要求。这肯定是需要加注的一个有用说明,因为在某些情况下它可能很重要,但考虑到PHP默认情况下就是弱类型语言,这就是问题的本质。没有必要假装否认这一点。你不是在寻找不同的解决方案,而是在寻找不同的编程语言。 - Conor Mancone
2
@JayBienvenu 简而言之,没有一个标准答案。编程总是一个平衡的过程,我认为你来这里批评所有答案都没有提供精确类型匹配并不太合理,因为默认情况下,语言通常不提供精确类型匹配。无论你是否意识到,你在抱怨 PHP,而不是这里的任何答案,这并没有真正帮助任何人。如果精确类型匹配是你认真对待的事情,那么你应该发布一个提供这种精确性的答案 - 的确,其他人可能需要它,所以看到这样的答案会很好。 - Conor Mancone
显示剩余5条评论

21

虽然有点晚了,但希望这对你有所帮助:

如果你确定两个数组中只包含字符串或只包含整数,则 array_count_values($a) == array_count_values($b) 的时间复杂度更好。但是,user1844933的答案更加通用。


1
如果整数存在冲突(即第一个值为-1,第二个值为+1),则此操作将失败,计算出的值将等于两个数组,但它们是不同的数组。 - kindaian
1
@kindaian请演示何时array_count_values()可能会生成负值。 - mickmackusa

8
如果您不想对数组进行排序,只想检查相等性而不考虑值的顺序,请使用http://php.net/manual/en/function.array-intersect.php,如下所示:
$array1 = array(2,5,3);
$array2 = array(5,2,3);
if($array1 === array_intersect($array1, $array2) && $array2 === array_intersect($array2, $array1)) {
    echo 'Equal';
} else {
    echo 'Not equal';
}

7
该解决方案没有考虑项目的重复。例如:[2,5,3]和[5,2,3,3]将根据上述逻辑相等。 - Nagendra Rao
您好,如果$array1 = array(2,5,3,1,9,8); $array2 = array(5,2,3); 那么它该如何使用? - Amol Navsupe
1
对于 [true][1],返回 true - Jay Bienvenu
@NagendraRao 请查看我处理重复测试用例的答案。https://dev59.com/G2Ei5IYBdhLWcg3wltIl#74504945 - steven7mwesigwa

5

由于所有与键完全无关的给定答案都无法处理重复值(例如[1,1,2]等于[1,2,2]),因此我编写了自己的代码。

这个变体不能处理多维数组。 它会检查两个数组是否包含完全相同的值,而不考虑它们的键和顺序,而且不修改任何参数。

function array_equals(array $either, array $other) : bool {
    foreach ($other as $element) {
        $key = array_search($element, $either, true);
        if ($key === false) {
            return false;
        }
        unset($either[$key]);
    }
    return empty($either);
}

虽然问题要求不使用 foreach 循环,但我没有找到任何满足我的要求且不使用循环的解决方案。此外,大多数其他常用函数也在内部使用循环。
为什么它不修改 $either 呢?因为在 PHP 中,值是按写入复制的,所以只要这是自己的函数而不是内联代码,一旦第一次修改 $either 参数,数组就会被复制。
如果你想内联这段代码,请在此之前执行此操作:
$either = $myarray;
// inline code from function above

这是唯一通过我的测试用例的解决方案:https://dev59.com/G2Ei5IYBdhLWcg3wltIl#73329182 - Sam Becker

5

2
这个解决方案没有考虑项目的重复。例如:[2,5,3]和[5,2,3,3]将根据上述逻辑相等。 https://3v4l.org/1SRp7 - dearsina
我最终做了 array_diff($arr1, $arr2) === array_diff($arr2, $arr1) && count($arr1) === count($arr2) - Chris
3
@Chris,即使你这样做仍然会给出[2,2,1]和[1,1,2]的误报。 - George Dimitriadis

2
将数组转换为字符串,然后比较字符串,这个怎么样?
sort($a);
sort($b);
$a_str = implode(",", $a);
$b_str = implode(",", $b);
f ( strcmp($a_str, $b_str) !== 0)
{
}

似乎对于包含逗号的字符串元素的数组,这种方法可能会失败。 - undefined

1
function array_equal($a, $b){
return count($a) === count($b) && empty(array_diff($a, $b)) && empty(array_diff($b, $a));
}

这是准确的解决方案。

1
说,如果你有两个定义为这样的数组:
$array1 = array(2,5,3);
$array2 = array(5,2,3);

然后,您可以使用以下代码来判断它们是否相等:
if(array_diff($array1,$array2) === array_diff($array2,$array1) &&count($array1)==count($array2))
{
    echo 'Equal';
}
else
{
    echo 'Not equal';
}

请在您的答案中添加一些解释。 - El.Hum

0

给出的答案包含各种怪癖,证明这不是一个简单的问题,并且在您的领域中定义“相同值”的含义非常重要。我对此的解决方案需要实现以下设计目标:

  • 两个数组都应被视为“列表”。
  • 该解决方案应适用于包含所有类型(包括对象,以与枚举等功能兼容)的数组。
  • 应支持重复元素。
  • 不起作用的情况应抛出异常。

评估解决方案的测试用例如下:

<?php

namespace App\Tests\Unit\Utility;

use App\Utility\ArrayUtil;
use PHPUnit\Framework\TestCase;

class ArrayUtilTest extends TestCase {

    /**
     * @dataProvider elementsEqualDataProviderTrueCases
     */
    public function test_elements_are_equal_true_cases(array $array1, array $array2): void {
        $originalArray1 = serialize($array1);
        $originalArray2 = serialize($array2);
        $this->assertTrue(ArrayUtil::elementsEqual($array1, $array2));
        $this->assertSame($originalArray1, serialize($array1));
        $this->assertSame($originalArray2, serialize($array2));
    }

    /**
     * @dataProvider elementsEqualDataProviderFalseCases
     */
    public function test_elements_are_equal_false_cases(array $array1, array $array2): void {
        $originalArray1 = serialize($array1);
        $originalArray2 = serialize($array2);
        $this->assertFalse(ArrayUtil::elementsEqual($array1, $array2));
        $this->assertSame($originalArray1, serialize($array1));
        $this->assertSame($originalArray2, serialize($array2));
    }

    /**
     * @dataProvider exceptionTestCases
     */
    public function test_elements_are_equal_exceptional_cases(mixed $array1, mixed $array2, string $exception): void {
        $this->expectException($exception);
        $originalArray1 = serialize($array1);
        $originalArray2 = serialize($array2);
        $this->assertFalse(ArrayUtil::elementsEqual($array1, $array2));
        $this->assertSame($originalArray1, serialize($array1));
        $this->assertSame($originalArray2, serialize($array2));
    }

    public function elementsEqualDataProviderTrueCases(): \Generator {
        yield 'Empty arrays' => [
            [],
            [],
        ];
        yield 'Integer types' => [
            [1, 2, 3],
            [3, 2, 1],
        ];
        yield 'Boolean types' => [
            [true, false],
            [false, true],
        ];
        yield 'String types' => [
            ["abc", "def"],
            ["def", "abc"],
        ];
        $objectA = new \stdClass();
        $objectB = new \stdClass();
        yield 'Object types' => [
            [$objectA, $objectB],
            [$objectB, $objectA],
        ];

        $objectC = new \stdClass();
        yield 'Mixed types' => [
            [2, true, "foo", null, $objectC],
            ["foo", null, 2, true, $objectC],
        ];
        yield 'Array types' => [
            [[1, 2], [3, 4]],
            [[3, 4], [1, 2]],
        ];
        yield 'Repeated values' => [
            [1, 1, 2],
            [2, 1, 1],
        ];
    }

    public function elementsEqualDataProviderFalseCases(): \Generator {
        yield 'Integer types' => [
            [1, 2, 3],
            [4, 5, 6],
        ];
        yield 'Boolean types' => [
            [true],
            [false],
        ];
        yield 'String types' => [
            ["abc", "def"],
            ["hij", "abc"],
        ];
        yield 'Object types' => [
            [new \stdClass(), new \stdClass()],
            [new \stdClass(), new \stdClass()],
        ];
        $objectC = new \stdClass();
        yield 'Mixed types' => [
            [2, false, "foo", null, $objectC],
            ["foo", null, 2, true, $objectC],
        ];
        yield 'Array types' => [
            [[1, 2], [3, 4]],
            [[4, 3], [2, 1]],
        ];
        yield 'Repeated values' => [
            [1, 1, 2],
            [2, 2, 1],
        ];
        yield 'Repeated values, longer second argument' => [
            [1, 1, 2],
            [2, 2, 1, 1],
        ];
        yield 'Repeated values, longer first argument' => [
            [1, 1, 2, 2],
            [2, 2, 1],
        ];
    }

    public function exceptionTestCases(): \Generator {
        yield 'Non-list array first argument' => [
            ['foo' => 'bar'],
            [1, 2, 3],
            \InvalidArgumentException::class,
        ];
        yield 'Non-list array second argument' => [
            [1, 2, 3],
            ['foo' => 'bar'],
            \InvalidArgumentException::class,
        ];
        yield 'Non array arguments' => [
            "foo",
            "bar",
            \TypeError::class,
        ];
    }
}

解决方案(主要改编自seyfahni的答案):

<?php

namespace App\Utility;

final class ArrayUtil {
    public static function elementsEqual(array $array1, array $array2): bool {
        if (!array_is_list($array1) || !array_is_list($array2)) {
            throw new \InvalidArgumentException('Arrays compared for element equality must both be lists.');
        }
        if (count($array1) !== count($array2)) {
            return false;
        }
        foreach ($array1 as $element) {
            $key = array_search($element, $array2, true);
            if ($key === false) {
                return false;
            }
            unset($array2[$key]);
        }
        return empty($array2);
    }
}

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