Reputation: 199
I have two arrays and these arrays contain information about id
, linklabel
and url
in the following format:
$pageids = [
['id' => 1, 'linklabel' => 'Home', 'url' => 'home'],
['id' => 2, 'linklabel' => 'Graphic Design', 'url' => 'graphicdesign'],
['id' => 3, 'linklabel' => 'Other Design', 'url' => 'otherdesign'],
['id' => 6, 'linklabel' => 'Logo Design', 'url' => 'logodesign'],
['id' => 15, 'linklabel' => 'Content Writing', 'url' => 'contentwriting'],
];
$parentpage = [
['id' => 2, 'linklabel' => 'Graphic Design', 'url' => 'graphicdesign'],
['id' => 3, 'linklabel' => 'Other Design', 'url' => 'otherdesign'],
];
I'm now trying to compare these two in order to find the information that is in $pageids
but NOT in $parentpage
- this will then make up another array called $pageWithNoChildren
. However when I use the following code:
$pageWithNoChildren = array_diff_assoc($pageids,$parentpage);
The array_diff_assoc()
runs on the first level of the arrays and therefore sees that both $pageids
and $parentpages
have a [0] and [1] key so it ignores them and returns all the information from $pageids
from [2] onwards. However I want it to look at the content of the nested arrays and compare those e.g. I need it to see which id
, linklabel
and url
are in $pageids
and not in $parentpages
and return those values.
How can I get the array_diff_assoc()
to run on the keys of the nested arrays and not the keys of the first arrays so the final result is an array that contains the contents of the [0], [3] and [4] arrays from $pageids
?
Expected Result:
array (
0 =>
array (
'id' => 1,
'linklabel' => 'Home',
'url' => 'home',
),
3 =>
array (
'id' => 6,
'linklabel' => 'Logo Design',
'url' => 'logodesign',
),
4 =>
array (
'id' => 15,
'linklabel' => 'Content Writing',
'url' => 'contentwriting',
),
)
Upvotes: 7
Views: 11332
Reputation: 47992
Most simply, call array_udiff()
to leverage a callback with a 3-way comparison (no iterated function calls, no serializing) on the rows.
This will still work if the rows have associative keys in different orders -- no sorting is necessary.
Code: (Demo)
$pageids = [
['id' => 1, 'linklabel' => 'Home', 'url' => 'home'],
['id' => 2, 'linklabel' => 'Graphic Design', 'url' => 'graphicdesign'],
['id' => 3, 'linklabel' => 'Other Design', 'url' => 'otherdesign'],
['id' => 6, 'linklabel' => 'Logo Design', 'url' => 'logodesign'],
['id' => 15, 'linklabel' => 'Content Writing', 'url' => 'contentwriting'],
];
$parentpage = [
['id' => 2, 'linklabel' => 'Graphic Design', 'url' => 'graphicdesign'],
['url' => 'otherdesign', 'id' => 3, 'linklabel' => 'Other Design'],
];
var_export(
array_udiff($pageids, $parentpage, fn($a, $b) => $a <=> $b)
);
Output:
array (
0 =>
array (
'id' => 1,
'linklabel' => 'Home',
'url' => 'home',
),
3 =>
array (
'id' => 6,
'linklabel' => 'Logo Design',
'url' => 'logodesign',
),
4 =>
array (
'id' => 15,
'linklabel' => 'Content Writing',
'url' => 'contentwriting',
),
)
Granted the asker's sample data does not indicate shuffled subarray keys, my sample input will disrupt @AbraCadaver's and @Lebnik's algorithms and cause them to give a different result than expected. My snippet gives the same result as @qdev's and @Gruber's answers, but my snippet does FAR less work.
Upvotes: 0
Reputation: 78994
To check multi-deminsions try something like this:
$pageWithNoChildren = array_map('unserialize',
array_diff(array_map('serialize', $pageids), array_map('serialize', $parentpage)));
array_map()
runs each sub-array of the main arrays through serialize()
which converts each sub-array into a string representation of that sub-array
array_diff()
now has a one-dimensional array for each of the arrays to comparearray_map()
runs the array result (differences) through unserialize()
to turn the string representations back into sub-arraysQ.E.D.
Upvotes: 23
Reputation: 2308
Excellent answer from @AbraCadaver and very observing remark from @qdev. My proposal is just a small adjustment to make the already proposed answer from @qdev more portable. Assigning the function to a variable make it usable also inside class methods and so on.
$sortAndSerialize = function ($arr)
{
ksort($arr);
return serialize($arr);
};
$pageWithNoChildren = array_map(
'unserialize',
array_diff(array_map($sortAndSerialize, $pageids),
array_map($sortAndSerialize, $parentpage))
);
Upvotes: 1
Reputation: 636
Right way https://github.com/yapro/helpers/blob/master/src/ArrayHelper.php
class ArrayHelper
{
/**
* @param array $array1
* @param array $array2
* @return array
*/
function arrayDiffAssocMultidimensional(array $array1, array $array2): array
{
$difference = [];
foreach ($array1 as $key => $value) {
if (is_array($value)) {
if (!array_key_exists($key, $array2)) {
$difference[$key] = $value;
} elseif (!is_array($array2[$key])) {
$difference[$key] = $value;
} else {
$multidimensionalDiff = $this->arrayDiffAssocMultidimensional($value, $array2[$key]);
if (count($multidimensionalDiff) > 0) {
$difference[$key] = $multidimensionalDiff;
}
}
} else {
if (!array_key_exists($key, $array2) || $array2[$key] !== $value) {
$difference[$key] = $value;
}
}
}
return $difference;
}
}
Upvotes: 3
Reputation: 1449
Very nice solution from @AbraCadaver, but like I've stated in the comments, there might be cases when the elements of associative arrays are not in the same order everywhere, thus a custom function which will sort them by index / key first is handy:
function sortAndSerialize($arr){
ksort($arr);
return serialize($arr);
}
array_map('unserialize', array_diff(array_map('sortAndSerialize', $pageids), array_map('sortAndSerialize', $parentpage)));
Upvotes: 5