I have two arrays, $a and $b here, and need to check if they contain exactly the same elements (independently of the order). I am thinking of using
if (sizeof($a)==sizeof($b) AND array_diff($a,$b)==array())
{
}
But I am new to PHP, so I wonder: Is there a better way?
Since I need to use them as sets, maybe I should not use arrays at all but something else.
The accepted answer is wrong! It will fail on: https://3v4l.org/U8U5p
$a = ['x' => 1, 'y' => 2]; $b = ['x' => 1, 'y' => 1];
Here is a correct solution:
function consistsOfTheSameValues(array $a, array $b)
{
// check size of both arrays
if (count($a) !== count($b)) {
return false;
}
foreach ($b as $key => $bValue) {
// check that expected value exists in the array
if (!in_array($bValue, $a, true)) {
return false;
}
// check that expected value occurs the same amount of times in both arrays
if (count(array_keys($a, $bValue, true)) !== count(array_keys($b, $bValue, true))) {
return false;
}
}
return true;
}
Plus quite extensive unit tests: https://3v4l.org/m6lHv
<?php
// A unit testing framework in a tweet. https://gist.github.com/mathiasverraes/9046427
function it($m,$p){echo ($p?'✔︎':'✘')." It $m\n"; if(!$p){$GLOBALS['f']=1;}}function done(){if(@$GLOBALS['f'])die(1);}
function consistsOfTheSameValues(array $a, array $b)
{
// check size of both arrays
if (count($a) !== count($b)) {
return false;
}
foreach ($b as $key => $bValue) {
// check that expected value exists in the array
if (!in_array($bValue, $a, true)) {
return false;
}
// check that expected value occurs the same amount of times in both arrays
if (count(array_keys($a, $bValue, true)) !== count(array_keys($b, $bValue, true))) {
return false;
}
}
return true;
}
it('consist of the same values',
consistsOfTheSameValues([1], [1]) === true
);
it('consist of the same values',
consistsOfTheSameValues([1, 1], [1, 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['1', 1], ['1', 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['1', 1], [1, '1']) === true
);
it('consist of the same values',
consistsOfTheSameValues([1, '1'], ['1', 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues([1, '1'], [1, '1']) === true
);
it('consist of the same values',
consistsOfTheSameValues(['x' => 1], ['x' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['x' => 1], ['y' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['y' => 1], ['x' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['x' => 1, 'y' => 1], ['x' => 1, 'y' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['y' => 1, 'x' => 1], ['x' => 1, 'y' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['x' => 1, 'y' => 1], ['y' => 1, 'x' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['y' => 1, 'x' => 1], ['y' => 1, 'x' => 1]) === true
);
it('consist of the same values',
consistsOfTheSameValues(['x' => 2, 'y' => 1], ['x' => 1, 'y' => 2]) === true
);
it('does not consist of the same values',
consistsOfTheSameValues([1], [2]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1'], [1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1], ['1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1], [1, 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, 1], [1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1', 1], [1, 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, '1'], [1, 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, 1], ['1', 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, 1], [1, '1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1', '1'], [1, 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1', '1'], ['1', 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1', '1'], [1, '1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, 1], ['1', '1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['1', 1], ['1', '1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues([1, '1'], ['1', '1']) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['x' => 1], ['x' => 2]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['x' => 1, 'y' => 1], ['x' => 1, 'y' => 2]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['x' => 1, 'y' => 1], ['x' => 2, 'y' => 1]) === false
);
it('does not consist of the same values',
consistsOfTheSameValues(['x' => 2, 'y' => 1], ['x' => 1, 'y' => 1]) === false
);
@update:
Extensive unit test of @ircmaxell answer: https://3v4l.org/5ivgm
Extensive unit test of @Jon anwser: https://3v4l.org/CrTgQ
Well, we can do something like this:
if (count(array_diff(array_merge($a, $b), array_intersect($a, $b))) === 0) {
//they are the same!
}
The reason it works, is that array_merge
will make a big array that has all the elements of both $a
and $b
(all the elements that are in either $a
, $b
, or both). array_intersect
will create an array that has all the elements that are in both $a
and $b
only. So if they are different,, there must be at least one element that does not appear in both arrays...
Also note that sizeof
is not an actual function/construct, it's an alias. I'd suggest using count()
for clarity...
Just for your amusement I'll add an example that demonstrates that your conditions is not correct:
<?php
$a = array(1, 1, 2);
$b = array(1, 2, 3);
var_dump(sizeof($a)==sizeof($b) AND array_diff($a,$b)==array());
?>
Test it.
I would suggest using a different model. Maybe adding the elements as keys of the array, but this is possible only if they are integers or strings.
$arr['itemA'] = true;
$arr['itemB'] = true;
This will enforce uniqueness. With this model you can use your condition on array_keys($arr)
.
If you think of the arrays as sets:
Then your approach is almost correct (you need to drop the equality test for the element count).
If it matters that the arrays contain multiple copies of the same element:
Then your approach is not correct. You need to sort the arrays with sort
and then compare them with ===
. This should be faster, as it can abort the comparison the moment it sees one difference without going over the whole arrays.
Update:
Clarified exactly when the OP's approach would be correct or not, also incorporated the suggestion that sort
would be probably better than asort
here.