递归array_diff()?

时间:2010-10-06 20:08:34

标签: php arrays recursion diff

我正在寻找一些工具来给我一个两个数组的递归diff。我设想的是一个带有两个颜色编码树结构的网页。在每棵树上,绿色是阵列的两个阵列中匹配的部分,而红色是每个树中与另一个不匹配的部分。类似于dBug的输出

我有一些代码可以为我提供一个嵌套数组来填充报表。我正在开发一个应该更快的新方法,但我需要测试值和结构,以确保它提供与旧方法相同的输出。

我可以使用哪些东西吗?或者我需要写这个吗?或者还有另一种方法来实现我的目标吗?

5 个答案:

答案 0 :(得分:57)

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

实现一次只处理两个数组,但我不认为这确实存在问题。如果一次需要3个或更多数组的diff,则可以按顺序运行diff。此方法也使用密钥检查并进行松散的验证。

答案 1 :(得分:9)

接受的答案接近正确,但它并没有真正模仿array_diff

有两个问题主要围绕密钥匹配:

  1. array_diff有一个特定的行为,如果第二个数组的仍然在第二个数组中,它就不会产生第二个数组完全丢失的数组键的结果。如果您有两个数组$first = ['foo' => 2, 'moo' => 2]$second = ['foo' => 2],则使用接受的答案函数,输出将为['moo' => 2]。如果通过array_diff运行相同的数组,它将生成一个空数组。这是因为如果数组键丢失,上面函数的最终else语句会将它添加到diff中,但这不是array_diff的预期行为。这两个数组也是如此:$first = ['foo' => 1]$second = [1]array_diff将生成一个空数组。

  2. 如果两个数组具有相同的值但键不同,则返回的值多于预期值。如果您有两个数组$foo = [1, 2]$moo = [2, 1],则接受的答案中的函数将输出$foo中的所有值。这是因为它在每次迭代时都进行严格的密钥匹配,它在两个数组中找到相同的密钥(数字或其他),而不是检查第二个数组中的所有其他值。

  3. 以下函数类似,但更接近于您期望array_diff工作的方式(也使用不太愚蠢的变量名称):

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

答案 2 :(得分:4)

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

示例:

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

<强>输出:

array_diff_assoc_recursive($a,$b);

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

答案 3 :(得分:2)

试试这段代码:

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

答案 4 :(得分:0)

Mohamad的回答很好,除了需要在线更改之外

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

具有:

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

或者,如果您使用的是Laravel,请使用:

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

否则,您将收到类似错误

  

PHP错误:未定义索引:some_key

在$ secondArray中存在some_key但在$ firstArray中不存在