53
[2,5,3]    

[5,2,3]

They are equal because they have the same values, but not in the same order.

Can I find that out without using a foreach() loop with in_array()? I don't think it would be efficient.

5
  • 1
    array_diff() is not perfect one. sort and than comparing the best. Commented Jan 15, 2014 at 13:28
  • 1
    @jeroen: run this:`$array = array(4,3); $array2 = array(2,3,4); echo var_dump(array_diff($array,$array2));' Commented Jan 15, 2014 at 13:30
  • @SureshKamrushi Nice! Commented Jan 15, 2014 at 13:32
  • so array_diff doesnt work? Commented Jan 15, 2014 at 17:05
  • 1
    As of the time that I am writing this, none of the given solutions actually work. All of them fail with boolean values. Commented Apr 30, 2018 at 22:22

12 Answers 12

95
sort($a);
sort($b);
if ($a===$b) {//equal}
Sign up to request clarification or add additional context in comments.

6 Comments

The sort function has a side effect of sorting the data so if you want to preserve the original order you need to make a copy of it into $a and $b before using this.
I would not accept this answer. Returns true for ['a'] and [true].
Of note, testing sort($a) == sort($b) always returns true, I assume because PHP is checking the return of sort() with is probably 1, instead of looking at the arrays.
Its not best answer, sort affect the array values. It's not working in all case
@JayBienvenu Tested this case with === instead of ==, ['a'] and [true] returns false.
|
36

Coming to this party late. I had the same question but didn't want to sort, which was the immediate answer I knew would work. I came up with this simple one-liner which only works for arrays of unique values:

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

It's also about a factor of 5 faster than the sort option. Not that either is especially slow, so I would say it is more about your personal preferences and which one you think is more clear. Personally I would rather not sort.

Edit: Thanks Ray for pointing out the fact that this only works with arrays with unique values.

10 Comments

I think this is the best answer, the accepted answer leaves a side effect of actually sorting arrays $a and $b which you might not want.
Looking a bit deeper this seems to not work in every case as array_diff collapses multiple results into one. This seems to pass. $a = ['red','red','blue','blue']; $b = ['red','red','red','blue'];
Returns true for [true] and [1].
Indeed @JayBienvenu, many PHP functions are not strict about types. It's certainly a helpful note to add because there might occasionally be uses cases where it matters, but considering that PHP is effectively loosely-typed by default, that is just the nature of the beast. No sense in pretending otherwise. You're not looking for a different solution, you're looking for a different language.
@JayBienvenu in short, there is never just one answer. Programming is always a balancing act, and I don't think it makes much sense for you to come here and criticize all the answers for not providing exact type matching in a language that, by default, doesn't usually provide exact type matching. Whether you realize it or not you are complaining about PHP, not any of the answers here, and that isn't really helping anyone. If exact type matching is something you take seriously then you should post an answer that provides that exactness - indeed, others may need it so it would be good to see.
|
22

This is a bit late to the party but in hopes that it will be useful:

If you are sure the arrays both only contain strings or both only contain integers, then array_count_values($a) == array_count_values($b) has better time complexity. However, user1844933's answer is more general.

4 Comments

This will fail if there is a clash in the integers (meaning the first value has -1, and the second has +1) the computed value will be equal to both arrays, but they are different arrays.
@kindaian please demonstrate when array_count_values() can possibly generate a negative value.
array_count_values does not work if 2 arrays have the same elements but in different orders.
@AliAkbarAzizi == between arrays does not check element order, only === does
9

If you don't want to sort arrays but just want to check equality regardless of value order use http://php.net/manual/en/function.array-intersect.php like so:

$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';
}

4 Comments

This solution doesn't take repetition of items into consideration. e.g.: [2,5,3] & [5,2,3,3] would be equal according to above logic.
hello sir, if $array1 = array(2,5,3,1,9,8); $array2 = array(5,2,3); then how is it used?
Returns true for [true] and [1].
@NagendraRao Check out my answer for handling repetition test cases. stackoverflow.com/a/74504945/7376590
8

As none of the given answers that are completely key-independent work with duplicated values (like [1,1,2] equals [1,2,2]) I've written my own.

This variant does not work with multi-dimensional arrays. It does check whether two arrays contain the exactly same values, regardless of their keys and order without modifying any of the arguments.

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);
}

Although the question asked about a foreach-free variant, I couldn't find any solution that satisfied my requirements without a loop. Additionally most of the otherwise used functions use a loop internally too.


Why does it not modify $either? Because values in PHP are copy-on-write, so as long as this is its own function and not inline code the array is copied once the $either argument is modified for the first time.

If you want to inline this code do this before:

$either = $myarray;
// inline code from function above

1 Comment

This was the only solution that passed my test case: stackoverflow.com/a/73329182/59730
6

The best way will be using array_diff http://php.net/manual/en/function.array-diff.php

$arr1 = [2,5,3];
$arr2 = [5,2,3];

$isEqual = array_diff($arr1,$arr2) === array_diff($arr2,$arr1);

3 Comments

This solution doesn't take repetition of items into consideration. e.g.: [2,5,3] & [5,2,3,3] would be equal according to above logic. 3v4l.org/1SRp7
I ended up doing array_diff($arr1, $arr2) === array_diff($arr2, $arr1) && count($arr1) === count($arr2)
@Chris that will still give you false positive for [2,2,1] and [1,1,2]
3

How about converting the arrays to strings and then comparing the strings.

sort($a);
sort($b);
$a_str = implode(",", $a);
$b_str = implode(",", $b);
f ( strcmp($a_str, $b_str) !== 0)
{
}

1 Comment

seems like this would fail for arrays containing string elements with commas in them
2

Say, if you have two arrays defined like this:

$array1 = array(2,5,3);
$array2 = array(5,2,3);

Then you can use this piece of code to judge whether they equal:

if(array_diff($array1,$array2) === array_diff($array2,$array1) &&count($array1)==count($array2))
{
    echo 'Equal';
}
else
{
    echo 'Not equal';
}

1 Comment

Please add some explanation to your answer
2
function array_equal($a, $b){
return count($a) === count($b) && empty(array_diff($a, $b)) && empty(array_diff($b, $a));
}

Here is the accurate solution

Comments

2

I have inspected all the answers here and came up with a uniform solution for most if not all test cases:

Solution:

if (!function_exists("array_same")) {
    function array_same(array $first, array $second): bool
    {
        return array_count_values(
                array_map("serialize", $first)
            )
            == array_count_values(
                array_map("serialize", $second)
            );
    }
}

Test Cases:


$print = fn(array $a, array $b, string $answer) => json_encode($a)
    . " === "
    . json_encode($b)
    . " = "
    . $answer;

$a = ['red', 'red', 'blue', 'blue'];
$b = ['red', 'blue', 'red', 'blue'];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "true".

echo PHP_EOL;

$a = ['red', 'red', 'blue', 'blue'];
$b = ['red', 'red', 'red', 'blue'];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "false".

echo PHP_EOL;

$a = ['a'];
$b = [true];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "false".

echo PHP_EOL;

$a = [true];
$b = [1];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "false".

echo PHP_EOL;

$a = [41.235, 'a', 41, 'a', 'b', true];
$b = ['a', 41.235, 'b', true, 'a', 41];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "true".

echo PHP_EOL;

$a = [new \stdClass()];
$b = [new \stdClass()];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "true".

echo PHP_EOL;

$a = [null];
$b = [false];
echo array_same($a, $b)
    ? $print($a, $b, 'true')
    : $print($a, $b, 'false'); // "false".

Comments

1

The given answers contain various quirks that prove this is not a straightforward problem and defining what "same values" means up-front for your domain is important. My solution for this required the following design goals:

  • Both arrays should be considered "lists".
  • The solution should work with arrays that contain all types (including objects, for compatibility with features like enums).
  • Repeated elements should be supported.
  • Cases that don't work should throw exceptions.

The test case to evaluate solutions looks like:

<?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,
        ];
    }
}

And the solution (adapted primarily from seyfahni's answer):

<?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);
    }
}

Comments

-3
$array1 = array(2,5,3);
$array2 = array(5,2,3);
$result = array_diff($array1, $array2);
if(empty($result))
{
   echo "Both arrays are equal.";
}
else
{
   echo "Both arrays are different.";
}

4 Comments

That's not correct. your code example will fail for $array1 = array(2,5,3, 3); $array2 = array(5,2,3); array_diff checks which values are in both arrays, it does not care how often they are in there.
@Matthias Huttar that is correct .. unfortunately I had to go the hard way to discover ..
Also, array_diff "compares array1 against one or more other arrays and returns the values in array1 that are not present in any of the other arrays." So if, for example, $array2 had more distinct values than $array1, the code in this answer would falsely report the arrays as equal. This is the point alluded to in the comment by @SureshKamrushi under the question.
could i suggest the usage of array_unique to this answer?

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.