7

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',
  ),
)
mickmackusa
  • 43,625
  • 12
  • 83
  • 136
Smokescreen
  • 189
  • 3
  • 4
  • 13

5 Answers5

22

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.

AbraCadaver
  • 78,200
  • 7
  • 66
  • 87
  • that's so awesome! Thankyou! As an aside I don't suppose you could give a quick explanation of this could you please? I haven't used unserialize or array_maps before and I'm looking them up now but if you could give a quick run down that would be amazing, either way thanks again!! – Smokescreen Mar 12 '14 at 15:04
  • I think I may have found the answer so for others reading this and who are curious I believe that serialize() turns the php keys and values into a string so you have a string in all the arrays of $pageids and a string in all the arrays of $parentpages. The array_diff function can then compare these strings and pick out the ones that are in $pageids and not in $parentpages and then the result is converted back into php vales using unserialize(). Exactly what I needed! Thanks again! :) – Smokescreen Mar 12 '14 at 15:19
  • Thanks! I've just read your step-by-step and have also found some documentation and what you said is pretty much what I had understood from what I read so it looks like I've got it :) Thanks again, this was great! – Smokescreen Mar 12 '14 at 15:22
  • nice answer, but this assumes that the associative arrays are all sorted the same (associative keys in the same order), otherwise this will not work as expected ... – qdev Aug 07 '16 at 11:49
  • This method helped me a lot, but in my case i had to take care of data type of every element in array, when the array its serialized, my id's in one array were int and string in the other, so not matches were found. – Antonycx Oct 26 '17 at 21:55
4

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)));
qdev
  • 1,371
  • 1
  • 15
  • 18
2

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;
    }
}
Lebnik
  • 628
  • 8
  • 11
1

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))
);
Gruber
  • 2,196
  • 5
  • 28
  • 50
0

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.

mickmackusa
  • 43,625
  • 12
  • 83
  • 136