Home » Php » php – Use array_diff_assoc() or get difference of multidimensional arrays

php – Use array_diff_assoc() or get difference of multidimensional arrays

Posted by: admin July 12, 2020 Leave a comment

Questions:

I’ve been struggling with what I think should be a really easy issue for sometime now and just can’t work out the answer.

I have two arrays and these arrays contain information about id, linklabel and url in the following format:

$pageids
--------
Array ( 
[0] => Array 
( [id] => 1 
  [linklabel] => Home 
  [url] => home )

[1] => Array 
( [id] => 2 
  [linklabel] => Graphic Design 
  [url] => graphicdesign ) 

[2] => Array 
( [id] => 3 
  [linklabel] => Other Design 
  [url] => otherdesign ) 

[3] => Array 
( [id] => 6 
  [linklabel] => Logo Design 
  [url] => logodesign ) 

[4] => Array 
( [id] => 15 
  [linklabel] => Content Writing 
  [url] => contentwriting ) 
) 


$parentpage
-----------
Array ( 
[0] => Array 
( [id] => 2 
  [linklabel] => Graphic Design 
  [url] => graphicdesign ) 

[1] => Array 
( [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?

How to&Answers:

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()
  • serialize() 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.

Answer:

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

Answer:

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

Answer:

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