2
\$\begingroup\$

For my application, I was using array_diff_assoc, when I noticed it was returning the wrong value. I was using multidimensional arrays, therefore I needed a array_diff_assoc_recursive method.

I Googled around and found a few, but they all only took 2 parameters. The official array_diff_assoc can take an infinite number of params. I wanted mine to, so I wrote my own array_diff_assoc_recursive function.

<?php
class Tools{
    /**
     * Recursive version of array_diff_assoc
     * Returns everything from $a that is not in $b or the other arguments
     *
     * @param $a The array to compare from
     * @param $b An array to compare against
     * @param ... More arrays to compare against
     *
     * @return An array with everything from $a that not in $b or the others
     */
    public static function array_diff_assoc_recursive($a, $b){
        // Get all of the "compare against" arrays
        $b = array_slice(func_get_args(), 1);
        // Initial return value
        $ret = array();

        // Loop over the "to" array and compare with the others
        foreach($a as $key=>$val){
            // We should compare type first
            $aType = gettype($val);
            // If it's an array, we recurse, otherwise we just compare with "==="
            $args = $aType === 'array' ? array($val) : true;

            // Let's see what we have to compare to
            foreach($b as $x){
                // If the key doesn't exist or the type is different,
                // then it's different, and our work here is done
                if(!array_key_exists($key, $x) || $aType !== gettype($x[$key])){
                    $ret[$key] = $val;
                    continue 2;
                }

                // If we are working with arrays, then we recurse
                if($aType === 'array'){
                    $args[] = $x[$key];
                }
                // Otherwise we just compare
                else{
                    $args = $args && $val === $x[$key];
                }
            }

            // This is where we call ourselves with all of the arrays we got passed
            if($aType === 'array'){
                $comp = call_user_func_array(array(get_called_class(), 'array_diff_assoc_recursive'), $args);
                // An empty array means we are equal :-)
                if(count($comp) > 0){
                    $ret[$key] = $comp;
                }
            }
            // If the values don't match, then we found a difference
            elseif(!$args){
                $ret[$key] = $val;
            }
        }
        return $ret;
    }
}

I was wondering what you thought of my attempt. It seems to work ok for my application, and in the few tests I tried with it.

DEMO: http://ideone.com/5GZ8Tn

\$\endgroup\$
6
  • 1
    \$\begingroup\$ Please clarify your expected result from this demo: 3v4l.org/E3Nol What qualifies as a "difference"? Is it a diff if one subsequent array is missing/mismatching a respective value? Is it is a diff only if all of the respective values are missing or a mismatch? \$\endgroup\$ Aug 10, 2020 at 14:04
  • \$\begingroup\$ @mickmackusa Oh! You seemed to have broken my code :-P That's clearly a case I didn't consider. This question is 7 years old, and I don't even remember why I needed a array_diff_assoc_recursive method. I don't think I am still working on the project that this was used in. Though, as you've shown the code does need a bit of work to do what it's supposed to do. Maybe if I am bored later, I can try to fix it. Whatever I was using this for, it seemed to be ok there, but I don't remember exactly what this was being used for :-) \$\endgroup\$
    – gen_Eric
    Aug 10, 2020 at 15:37
  • 1
    \$\begingroup\$ You don't necessarily need to fix it. This is a fun thought exercise. Just tell me the intended behaviour/outcome. I spent a few hours yesterday refining your static method and implementing some modern techniques ...and only discovered the issue when testing my code. It was only at that point that I tested your code. \$\endgroup\$ Aug 10, 2020 at 21:07
  • \$\begingroup\$ @mickmackusa I guess the intended outcome would be whatever the official array_diff_assoc does, but then digs into arrays and compares individual elements using the same array_diff_assoc logic... \$\endgroup\$
    – gen_Eric
    Aug 11, 2020 at 15:01
  • \$\begingroup\$ The docs for array_diff_assoc say "the values from array1 that are not present in any of the other arrays." So, if it finds the value in any of the comparative arrays, it's removed from the first array. With my array_diff_assoc_recursive method if it sees an array as a value, it calls itself again. I'd think that your data should just be [8,10]... \$\endgroup\$
    – gen_Eric
    Aug 14, 2020 at 17:43

1 Answer 1

4
+50
\$\begingroup\$

The logic looks good but I would like to suggest a few tips to enhance readability and maintainability of your code.

First off: name your variables accordingly $a doesn't say anything to any random person when reading the code. He will have to scroll up and down figuring out what is what.

Second: Split your function into multiple methods. This will enhance readability and will keep an overview of your code. Now you have one very large method which will take a random person at least half an hour to figure out what it all does exactly.

Third: Try to avoid things like continue 2;, break; and more. They don't mean anything when you read code and can be avoided in most cases.

Fourth: Comments rot very quickly! Try and make sure that your methods are selfexplanatory so that you dont need to put comments.

\$\endgroup\$
1
  • 1
    \$\begingroup\$ Suggestion 2 is good and makes suggestion 1 less important. Suggestion 3 is wrong - however you can use a break variable and check for it instead.. may be friendlier on the eye for people who can't see the level control your code used. Suggestion 4 is valid, sadly, but still put comments. \$\endgroup\$
    – user56078
    Oct 14, 2014 at 22:40

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.