Question

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',
  ),
)
Was it helpful?

Solution

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
    • the main arrays now have values that are not arrays but string representations of the sub-arrays
  • array_diff() now has a one-dimensional array for each of the arrays to compare
  • after the difference is returned array_map() runs the array result (differences) through unserialize() to turn the string representations back into sub-arrays

Q.E.D.

OTHER TIPS

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

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

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

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top