50
[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.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Anna K.
  • 1,887
  • 6
  • 26
  • 38

13 Answers13

85
sort($a);
sort($b);
if ($a===$b) {//equal}
user1844933
  • 3,296
  • 2
  • 25
  • 42
  • 1
    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. – Ray Sep 01 '17 at 19:29
  • 18
    I would not accept this answer. Returns true for `['a']` and `[true]`. – Jay Bienvenu Apr 30 '18 at 22:03
  • 2
    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. – siliconrockstar Nov 24 '18 at 17:24
  • 2
    Its not best answer, sort affect the array values. It's not working in all case – Ali Akbar Azizi Feb 06 '19 at 20:33
  • 4
    @JayBienvenu Tested this case with `===` instead of `==`, `['a']` and `[true]` returns false. – Cameron Wilby Apr 19 '19 at 14:20
  • 1
    I'd upvote if it were `===`, but without that extra = this just isn't the most correct answer – Chris Jan 28 '21 at 11:12
30

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.

Conor Mancone
  • 1,940
  • 16
  • 22
  • 1
    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. – Ray Sep 01 '17 at 19:26
  • 3
    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']; – Ray Sep 01 '17 at 19:47
  • That makes me so sad... I'll have to check it out. I really don't want to have to do any sorting, but I also don't want to throw a `unique` on `$a` and `$b` to do this... – Conor Mancone Sep 01 '17 at 20:05
  • Indeed... unique doesn't get you out of this one either. I've only used this in use-cases where duplicates have already been removed, so this one slipped past my radar. I'm going to add a note about this important exception. – Conor Mancone Sep 01 '17 at 20:11
  • 1
    Returns true for `[true]` and `[1]`. – Jay Bienvenu Apr 30 '18 at 22:07
  • 2
    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. – Conor Mancone May 01 '18 at 20:43
  • If by "you're looking for another language" you mean one that doesn't make a lot if internal assumptions about what certain data values mean, then yes that's correct. Either way it's completely unhelpful. – Jay Bienvenu May 02 '18 at 17:52
  • @JayBienvenu Whether or not it is unhelpful or not depends on your needs. I've used this little bit of code in PHP for years now and never once had a problem with it because I have always used it to strictly compare integers. I could certainly write a function that checked for exact type matches, but due to the nature of PHP it would probably have worse performance than any of the above. Therefore, in all of my use cases, the hypothetical method that you are criticizing us for not providing would be a worse fit for my needs. – Conor Mancone May 03 '18 at 16:01
  • 2
    @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. – Conor Mancone May 03 '18 at 16:04
  • @JayBienvenu Check out my answer for your test case (`[true]` and `[1]`). https://stackoverflow.com/a/74504945/7376590 – steven7mwesigwa Nov 20 '22 at 01:11
20

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.

TwiNight
  • 1,852
  • 1
  • 17
  • 27
  • 1
    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 Aug 04 '17 at 10:05
  • 1
    @kindaian please demonstrate when `array_count_values()` can possibly generate a negative value. – mickmackusa Apr 12 '19 at 02:20
8

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';
}
whitebrow
  • 2,015
  • 21
  • 24
5

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);
Tamim
  • 916
  • 11
  • 19
  • 2
    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. https://3v4l.org/1SRp7 – dearsina Jul 01 '19 at 08:16
  • I ended up doing `array_diff($arr1, $arr2) === array_diff($arr2, $arr1) && count($arr1) === count($arr2)` – Chris Jan 28 '21 at 10:56
  • 3
    @Chris that will still give you false positive for [2,2,1] and [1,1,2] – George Dimitriadis Apr 05 '21 at 15:26
5

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
seyfahni
  • 161
  • 3
  • 8
2

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)
{
}
Fred Andrews
  • 648
  • 9
  • 18
1

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';
}
jishnu
  • 11
  • 1
1
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

0

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);
    }
}
Sam Becker
  • 19,231
  • 14
  • 60
  • 80
0

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".
steven7mwesigwa
  • 5,701
  • 3
  • 20
  • 34
-2

I came across this problem and solve it thus: I needed to ensure that two objects had the same fields So

const expectedFields = ['auth', 'message'];
const receivedFields = Object.keys(data);
const everyItemexists = expectedFields.map(i => receivedFields.indexOf(i) > -1);
const condition = everyItemexists.reduce((accumulator, item) => item && accumulator, true);

Basically, go through one of the arrays, here (I'm assuming there are of the same size though). Then check if its exists in the other array. Then i reduce the result of that.

IdiakosE Sunday
  • 112
  • 1
  • 8
-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.";
}
Rubin Porwal
  • 3,736
  • 1
  • 23
  • 26
  • 9
    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 Apr 16 '15 at 12:48
  • @Matthias Huttar that is correct .. unfortunately I had to go the hard way to discover .. – ion May 19 '15 at 00:27
  • 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. – faintsignal Dec 03 '15 at 16:22
  • 1
    could i suggest the usage of array_unique to this answer? – John Jun 15 '18 at 10:17