[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.
[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.
sort($a);
sort($b);
if ($a===$b) {//equal}
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.
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.
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';
}
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);
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
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)
{
}
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';
}
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
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:
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);
}
}
I have inspected all the answers here and came up with a uniform solution for most if not all test cases:
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)
);
}
}
$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".
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.
$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.";
}