2010-10-06 2 views
25

Je cherche un outil pour me donner un diff récursif de deux tableaux. Ce que j'imagine est une page web avec deux structures arborescentes codées en couleur. Sur chaque arbre, vert sont des parties du tableau qui correspondent dans les deux tableaux, et le rouge est pour les parties de chacun qui ne correspondent pas à l'autre. Quelque chose comme la sortie de dBugrecursive array_diff()?

J'ai du code qui me donne un tableau imbriqué pour remplir un rapport. Je suis en train de développer une nouvelle méthode qui devrait être plus rapide, mais j'ai besoin de tester les valeurs et aussi la structure, pour m'assurer qu'elle donne une sortie identique à l'ancienne méthode.

Y at-il quelque chose que je peux utiliser? Ou dois-je écrire cela? Ou y a-t-il un autre moyen d'atteindre mes objectifs?

+0

est-il _just_ pour tester ces sorties temporairement ou pour une utilisation plus? C'est pour un test, un simple 'wdiff' sur la sortie' var_export' devrait faire l'affaire ... – Wrikken

+0

Dans la structure imbriquée, si un élément est un tableau de 6, alors que l'autre est un tableau de 3, ce voyage jusqu'à 'wdiff'? Parce que dans la sortie, disons des lignes 0-30, elle sera identique, et de la fin à la ligne 36, elle sera identique. Ce sont seulement les lignes médianes qui seront différentes - 3 contre 6. Si wdiff regarde cela, va-t-il se faire trébucher? – user151841

+0

La sortie ne va pas être assez divisée en paires clé/valeur, il faudra cependant essayer de faire correspondre les lignes avant et après pour un match plus loin, et à mon humble avis, si je ne fais que vérifier, ça va. Il suffit d'utiliser un simple [testcript ici] (http://pastebin.com/wrwXw5zT) et de voir si est assez bon pour votre but. L'alternative est une fonction récursive, pas si difficile non plus, mais plus de travail. – Wrikken

Répondre

48

Il existe une telle fonction implémentée dans les commentaires de 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; 
} 

La mise en œuvre ne gère que deux tableaux à la fois, mais je ne pense pas que Posses vraiment un problème. Vous pouvez exécuter le diff de manière séquentielle si vous avez besoin du diff de 3 ou plus de tableaux à la fois. Cette méthode utilise également des vérifications de clé et effectue une vérification approximative.

+0

cela fonctionne. Merci pour cela. –

+0

@Zend_Sklave, Puisque la réponse de mhitza a fonctionné pour vous, vous devriez peut-être le marquer comme répondant réellement à votre demande ... –

+6

@JonL. Je pense qu'il devrait être l'auteur de la question afin de le faire :) – user151841

7

La réponse acceptée est proche de corriger, mais il n'émule pas vraiment array_diff correctement.

Il y a deux problèmes qui tournent en grande partie autour de l'appariement clé:

  1. array_diff a un comportement spécifique où il ne produit pas de résultat pour une clé de tableau qui est complètement absent de la deuxième matrice si sa valeur est toujours dans le deuxième tableau. Si vous avez deux tableaux $first = ['foo' => 2, 'moo' => 2] et $second = ['foo' => 2], en utilisant la fonction de réponse acceptée, la sortie sera ['moo' => 2]. Si vous exécutez les mêmes tableaux via array_diff, il va produire un tableau vide. En effet, l'instruction else finale de la fonction ci-dessus l'ajoute au diff si la clé du tableau est manquante, mais ce n'est pas le comportement attendu de array_diff. La même chose est vraie avec ces deux tableaux: $first = ['foo' => 1] et $second = [1]. array_diff va produire un tableau vide.

  2. Si deux tableaux ont les mêmes valeurs mais des clés différentes, il renvoie plus de valeurs que prévu. Si vous avez deux tableaux $foo = [1, 2] et $moo = [2, 1], la fonction de la réponse acceptée affichera toutes les valeurs de $foo. C'est parce qu'il fait une correspondance de clé stricte à chaque itération où il trouve la même clé (numérique ou autre) dans les deux tableaux au lieu de vérifier toutes les autres valeurs dans le second tableau.

La fonction suivante est similaire, mais agit de plus près à la façon dont vous attendez array_diff à travailler (aussi avec les noms des variables moins stupides):

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

Pourriez-vous expliquer comment les résultats sont différents? Pour mes tests, j'obtiens exactement les mêmes résultats. Merci –

+1

@JeffPuckettII désolé je n'ai pas donné une bonne explication. J'ai mis à jour la réponse pour expliquer en quoi la réponse acceptée diffère de 'array_diff'. – treeface

+1

Juste vérifié l'exemple simple à http://php.net/manual/en/function.array-diff.php et il fonctionne (se comporte) comme prévu (EDIT) pour les tableaux simples. Mais récursive ne fonctionne pas comme il se doit. :( – cottton

2

Essayez ce 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); 
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; 
} 

Exemple:

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

Sortie:

array_diff_assoc_recursive($a,$b); 

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