web-dev-qa-db-fra.com

array_diff () récursif?

Je cherche un outil pour me donner un diff récursif de deux tableaux. Ce que j'envisage, c'est une page Web avec deux arborescences à code couleur. Sur chaque arbre, le vert est 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 dBug

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 je dois tester les valeurs et également la structure, pour m'assurer qu'elle donne une sortie identique à l'ancienne méthode.

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

35
user151841

Il y a 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;
} 

L'implémentation ne gère que deux tableaux à la fois, mais je ne pense pas que cela pose vraiment de problème. Vous pouvez exécuter le diff séquentiellement si vous avez besoin du diff de 3 tableaux ou plus à la fois. Cette méthode utilise également des vérifications de clés et effectue une vérification lâche.

61
mhitza

La réponse acceptée est presque correcte, mais elle n'émule pas vraiment array_diff correctement.

Il existe deux problèmes qui tournent en grande partie autour de la correspondance des clés:

  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 manquante dans le deuxième tableau si son 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 produira un tableau vide. Cela est dû au fait que 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 produira 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. En effet, il effectue 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 du deuxième tableau.

La fonction suivante est similaire, mais agit plus étroitement sur la façon dont vous vous attendez array_diff pour fonctionner (également avec des noms de 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;
}
12
treeface
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
                )    
        )    
)
4
JRodryB

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);
2
M Rostami

La réponse de Mohamad fonctionne bien, sauf qu'elle doit être modifiée sur la ligne:

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

avec:

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

ou, si vous utilisez Laravel, avec:

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

Sinon, vous obtiendrez des erreurs comme

Erreur PHP: index non défini: une_clé

lorsqu'une clé_un existe dans $ secondArray mais pas dans $ firstArray

0
jfeid