2010-10-06 32 views
25

我正在尋找一些工具來給我一個兩個數組的遞歸差異。我設想的是一個帶有兩種顏色編碼的樹狀結構的網頁。在每棵樹上,綠色都是數組中與兩個數組相匹配的部分,紅色表示每個數組中不匹配的部分。類似於dBug的輸出recursive array_diff()?

我有一些代碼給了我一個嵌套數組來填充報表。我正在開發一種應該更快的新方法,但我需要測試值和結構,以確保它的輸出與舊方法相同。

我有什麼可以使用的東西嗎?或者我需要寫這個?還是有另一種方法來實現我的目標?

+0

難道_just_臨時測試這些輸出,或更長的使用?它是一個測試,一個簡單的'wdiff'超過'var_export'輸出應該做的訣竅... – Wrikken

+0

在嵌套結構中,如果一個元素是一個數組6,而另一個是數組3,那麼這次旅行'wdiff'?因爲在輸出中,從0到30行將是相同的,並且從結尾回到36行它將是相同的。只有那些中線會有所不同 - 3對6.如果wdiff看這個,它會被絆倒嗎? – user151841

+0

輸出不會在鍵/值對中漂亮地分割,然而它會盡力匹配之前和之後的線以進一步匹配,而恕我直言,如果我只是區分檢查,它會這樣做。只需使用一個簡單的[testscript here](http://pastebin.com/wrwXw5zT),看看你的目的是否足夠好。另一種選擇是遞歸函數,但並不那麼困難,但是更多的工作。 – Wrikken

回答

48

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個或更多數組的差異,則可以按順序運行差異。此外,此方法使用密鑰檢查並進行寬鬆驗證。

+0

它的工作原理。感謝這一點。 –

+0

@Zend_Sklave,因爲mhitza的答案爲你工作,你應該把它標記爲實際回答你的請求... –

+6

@JonL。我認爲他必須成爲這個問題的作者:) – user151841

7

接受的答案接近正確,但它並沒有真正模擬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的所有值。這是因爲它在每次迭代時都進行嚴格的密鑰匹配,在兩次數組中找到相同的鍵(數字或其他)而不是檢查第二個數組中的所有其他值。

下面的函數是相似的,但更緊密的作用是你期待怎樣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; 
} 
+0

你能解釋結果有什麼不同嗎?對於我的測試,我得到完全相同的結果。謝謝 –

+1

@JeffPuckettII對不起,我沒有給出一個很好的解釋。我已經更新了答案,以解釋接受的答案與array_diff的不同之處。 – treeface

+1

剛剛在http://php.net/manual/en/function.array-diff.php 上檢查了一個簡單示例,它對簡單數組的作用(行爲)如預期的那樣(編輯)。 但遞歸不能正常工作。 :( – cottton

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