Diffing, flattening and expanding multidimensional arrays in PHP

By · Published · php

PHP has functions that can compute the difference between two arrays built in. The comments sections for those functions are filled with people trying to figure out the best way to do the same thing with multidimensional arrays, and almost all of them are recursive diffing functions that try to walk the tree and do a diff at each level.

The problem with this approach are 1) they are unreliable as they usually don't account for all data types at each level, and 2) they're slow, due to multiple calls to array_diff at each level of the tree.

A better approach, I think, is to flatten a multidimensional array into a single dimension, make a single call to array_diff, then (if needed) expand it back out if you really need the resulting diff to be multidimensional.

Lets look at some code. The following recursive function flattens a multidimensional array into a single dimension.

<?php  
function flatten($arr, $base = "", $divider_char = "/") {
    $ret = array();
    if(is_array($arr)) {
        foreach($arr as $k = $v) {
            if(is_array($v)) {
                $tmp_array = flatten($v, $base.$k.$divider_char, $divider_char);
                $ret = array_merge($ret, $tmp_array);
            } else {
                $ret[$base.$k] = $v;
            }
        }
    }
    return $ret;
}
?>

The following function (based on this function found here) reinflates the array back up after it's been deflated.

<?php  
function inflate($arr, $divider_char = "/") {
    if(!is_array($arr)) {
        return false;
    }

    $split = '/' . preg_quote($divider_char, '/') . '/';

    $ret = array();
    foreach ($arr as $key = $val) {
        $parts = preg_split($split, $key, -1, PREG_SPLIT_NO_EMPTY);
        $leafpart = array_pop($parts);
        $parent = &$ret;
        foreach ($parts as $part) {
            if (!isset($parent[$part])) {
                $parent[$part] = array();
            } elseif (!is_array($parent[$part])) {
                $parent[$part] = array();
            }
            $parent = &$parent[$part];
        }

        if (empty($parent[$leafpart])) {
            $parent[$leafpart] = $val;
        }
    }
    return $ret;
}
?>

Now, with arrays in flat form, it's easy to use the built-in functions to diff:

<?php   
$arr1_flat = array();
$arr2_flat = array();

$arr1_flat = flatten($arr1);
$arr2_flat = flatten($arr2);

$ret = array_diff_assoc($arr1_flat, $arr2_flat);

$diff = inflate($ret);
?>
( Comments )

Did something I wrote help you out?

That's great! I don't earn any money from this site - I run no ads, sell no products and participate in no affiliate programs. I do this solely because it's fun; I enjoy writing and sharing what I learn.

All the same, if you found this article helpful and want to show your appreciation, here's my Amazon.com wishlist.


Related Posts

Harvesting Nest Thermostat Data For Fun And Profit

More collectd and pfSense Fun!


comments powered by Disqus