recursive array_diff()?

ε祈祈猫儿з 提交于 2019-11-26 11:52:48

There is one such function implemented in the comments of array_diff.

function arrayRecursiveDiff($aArray1, $aArray2) {
  $aReturn = array();

  foreach ($aArray1 as $mKey => $mValue) {
    if (array_key_exists($mKey, $aArray2)) {
      if (is_array($mValue)) {
        $aRecursiveDiff = arrayRecursiveDiff($mValue, $aArray2[$mKey]);
        if (count($aRecursiveDiff)) { $aReturn[$mKey] = $aRecursiveDiff; }
      } else {
        if ($mValue != $aArray2[$mKey]) {
          $aReturn[$mKey] = $mValue;
        }
      }
    } else {
      $aReturn[$mKey] = $mValue;
    }
  }
  return $aReturn;
} 

The implementation only handles two arrays at a time, but I do not think that really posses a problem. You could run the diff sequentially if you need the diff of 3 or more arrays at a time. Also this method uses key checks and does a loose verification.

The accepted answer is close to correct, but it doesn't really emulate array_diff correctly.

There are two problems that largely revolve around key matching:

  1. array_diff has a specific behavior where it does not produce a result for an array key that is completely missing from the second array if its value is still in the second array. If you have two arrays $first = ['foo' => 2, 'moo' => 2] and $second = ['foo' => 2], using the accepted answer's function the output will be ['moo' => 2]. If you run the same arrays through array_diff, it will produce an empty array. This is because the above function's final else statement adds it to the diff if the array key is missing, but that's not the expected behavior from array_diff. The same is true with these two arrays: $first = ['foo' => 1] and $second = [1]. array_diff will produce an empty array.

  2. If two arrays have the same values but different keys, it returns more values than expected. If you have two arrays $foo = [1, 2] and $moo = [2, 1], the function from the accepted answer will output all values from $foo. This is because it's doing a strict key matching on each iteration where it finds the same key (numerical or otherwise) in both arrays instead of checking all of the other values in the second array.

The following function is similar, but acts more closely to how you'd expect array_diff to work (also with less silly variable names):

function array_diff_recursive($arr1, $arr2)
{
    $outputDiff = [];

    foreach ($arr1 as $key => $value)
    {
        //if the key exists in the second array, recursively call this function 
        //if it is an array, otherwise check if the value is in arr2
        if (array_key_exists($key, $arr2))
        {
            if (is_array($value))
            {
                $recursiveDiff = array_diff_recursive($value, $arr2[$key]);

                if (count($recursiveDiff))
                {
                    $outputDiff[$key] = $recursiveDiff;
                }
            }
            else if (!in_array($value, $arr2))
            {
                $outputDiff[$key] = $value;
            }
        }
        //if the key is not in the second array, check if the value is in 
        //the second array (this is a quirk of how array_diff works)
        else if (!in_array($value, $arr2))
        {
            $outputDiff[$key] = $value;
        }
    }

    return $outputDiff;
}
function array_diff_assoc_recursive($array1, $array2)
{
    foreach($array1 as $key => $value){

        if(is_array($value)){
            if(!isset($array2[$key]))
            {
                $difference[$key] = $value;
            }
            elseif(!is_array($array2[$key]))
            {
                $difference[$key] = $value;
            }
            else
            {
                $new_diff = array_diff_assoc_recursive($value, $array2[$key]);
                if($new_diff != FALSE)
                {
                    $difference[$key] = $new_diff;
                }
            }
        }
        elseif((!isset($array2[$key]) || $array2[$key] != $value) && !($array2[$key]===null && $value===null))
        {
            $difference[$key] = $value;
        }
    }
    return !isset($difference) ? 0 : $difference;
}

Example:

$a = array(
    "product_a" => array(
        'description'=>'Product A',
        'color'=>'Red',
        'quantity'=>'5',
        'serial'=>array(1,2,3)
    ),
    "product_b" => array(
        'description'=>'Product B'
    )
);

$b = array(
    "product_a" => array(
        'description'=>'Product A',
        'color'=>'Blue',
        'quantity'=>'5',
        'serial'=>array(1,2,5)
    ),
    "product_b" => array(
        'description'=>'Product B'
    )
);

Output:

array_diff_assoc_recursive($a,$b);

Array
(
    [product_a] => Array
        (
            [color] => Red
            [serial] => Array
                (
                    [2] => 3
                )    
        )    
)

Try this code:

function arrayDiffRecursive($firstArray, $secondArray, $reverseKey = false)
{
    $oldKey = 'old';
    $newKey = 'new';
    if ($reverseKey) {
        $oldKey = 'new';
        $newKey = 'old';
    }
    $difference = [];
    foreach ($firstArray as $firstKey => $firstValue) {
        if (is_array($firstValue)) {
            if (!array_key_exists($firstKey, $secondArray) || !is_array($secondArray[$firstKey])) {
                $difference[$oldKey][$firstKey] = $firstValue;
                $difference[$newKey][$firstKey] = '';
            } else {
                $newDiff = arrayDiffRecursive($firstValue, $secondArray[$firstKey], $reverseKey);
                if (!empty($newDiff)) {
                    $difference[$oldKey][$firstKey] = $newDiff[$oldKey];
                    $difference[$newKey][$firstKey] = $newDiff[$newKey];
                }
            }
        } else {
            if (!array_key_exists($firstKey, $secondArray) || $secondArray[$firstKey] != $firstValue) {
                $difference[$oldKey][$firstKey] = $firstValue;
                $difference[$newKey][$firstKey] = $secondArray[$firstKey];
            }
        }
    }
    return $difference;
}

$differences = array_replace_recursive(
    arrayDiffRecursive($firstArray, $secondArray),
    arrayDiffRecursive($secondArray, $firstArray, true)
);
var_dump($differences);

The answer by Mohamad is working good, except that it needs change on the line:

$difference[$newKey][$firstKey] = $secondArray[$firstKey];

with:

$difference[$newKey][$firstKey] = array_key_exists($firstKey, $secondArray) ? $secondArray[$firstKey] : null;

or, if you are using Laravel, with:

$difference[$newKey][$firstKey] = array_get($secondArray, $firstKey);

Otherwise, you will get errors like

PHP error: Undefined index: some_key

when a some_key exists in $secondArray but not in $firstArray

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!