2010-10-06 14 views
25

Szukam jakiegoś narzędzia, które da mi rekursywną różnicę dwóch tablic. To, co sobie wyobrażam, to strona internetowa z dwiema kolorowymi strukturami drzewiastymi. Na każdym drzewie kolor zielony jest częścią tablicy, która pasuje do obu tablic, a czerwień do części każdego, która nie pasuje do drugiej. Coś jak wyjście z dBugrekursywna array_diff()?

Mam trochę kodu, który daje mi zagnieżdżonej tablicy do wypełnienia raportu. Opracowuję nową metodę, która powinna być szybsza, ale muszę przetestować wartości, a także strukturę, aby upewnić się, że daje wynik identyczny ze starą metodą.

Czy jest coś, czego mogę użyć? Czy muszę to napisać? Czy istnieje inny sposób na osiągnięcie moich celów?

+0

Czy _just_ do testowania tych wyjść tymczasowo lub na dłuższy użytku? Jest to test, proste wyjście 'wdiff' ponad' wyjście_zmiennej' powinno wystarczyć ... – Wrikken

+0

W strukturze zagnieżdżonej, jeśli jeden element jest tablicą 6, a drugi jest tablicą 3, to ta podróż do góry 'wdiff'? Ponieważ na wyjściu, powiedzmy z linii 0-30, będzie on identyczny, a od końca do linii 36 będzie identyczny. To tylko te środkowe linie będą się różnić - 3 w stosunku do 6. Jeśli wdiff się temu przyjrzy, czy się potknie? – user151841

+0

Dane wyjściowe nie będą dość rozdzielone w parach klucz/wartość, będą jednak starały się dopasować wiersze przed i po dla dalszego dopasowania, a IMHO, jeśli chcę tylko sprawdzić, to zrobi. Po prostu skorzystaj z prostego [testcript tutaj] (http://pastebin.com/wrwXw5zT) i sprawdź, czy jest wystarczająco dobry dla twojego celu. Alternatywą jest funkcja rekursywna, nie tak trudne, ale więcej pracy. – Wrikken

Odpowiedz

48

Jest jedna taka funkcja zaimplementowana w komentarzach 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; 
} 

Wdrożenie obsługuje tylko dwie tablice naraz, ale nie sądzę, że naprawdę ma problem. Możesz uruchomić diff sekwencyjnie, jeśli potrzebujesz diff 3 lub więcej tablic na raz. Ta metoda wykorzystuje również kontrole kluczy i wykonuje luźną weryfikację.

+0

to działa. dzięki za to. –

+0

@Zend_Sklave, ponieważ odpowiedź mhitza działała dla ciebie, powinieneś oznaczyć ją jako faktycznie odpowiadającą twoją prośbą ... –

+6

@ JonL. Myślę, że musiałby być autorem pytania, aby to zrobić :) – user151841

7

Przyjęta odpowiedź jest bliska poprawności, ale nie jest to właściwa emulacja array_diff.

Istnieją dwa problemy, które w dużej mierze koncentrują się wokół pasującego klucza:

  1. array_diff ma specyficzne zachowanie, gdzie nie wywołuje efektu na klucz tablicy, która jest całkowicie brakuje drugiego układu, jeżeli jego wartość jest nadal w drugiej tablicy. Jeśli masz dwie macierze $first = ['foo' => 2, 'moo' => 2] i $second = ['foo' => 2], używając funkcji akceptowanej odpowiedzi, wyjście będzie miało postać ['moo' => 2]. Jeśli uruchomisz te same tablice przez array_diff, zostanie utworzona pusta tablica. Dzieje się tak dlatego, że ostatnia instrukcja funkcji else dodaje ją do pliku różnic, jeśli brakuje klucza tablicy, ale nie jest to oczekiwane zachowanie z array_diff. To samo dotyczy tych dwóch tablic: $first = ['foo' => 1] i $second = [1]. array_diff spowoduje wyświetlenie pustej tablicy.

  2. Jeśli dwie macierze mają te same wartości, ale różne klucze, zwraca więcej wartości, niż oczekiwano. Jeśli masz dwie macierze $foo = [1, 2] i $moo = [2, 1], funkcja z zaakceptowanej odpowiedzi wypisze wszystkie wartości z $foo. Dzieje się tak dlatego, że dokonuje on ścisłego dopasowywania klucza w każdej iteracji, w której znajduje ten sam klucz (numeryczny lub inny) w obu tablicach, zamiast sprawdzać wszystkie inne wartości w drugiej macierzy.

Poniższa funkcja jest podobna, ale działa bardziej dokładnie, w jaki sposób można się spodziewać array_diff do pracy (również z mniej głupich nazw zmiennych):

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

Czy możesz wyjaśnić, jak wyniki są różne? W przypadku moich testów otrzymuję dokładnie takie same wyniki. Dzięki –

+1

@JeffPuckettII przepraszam, że nie podałem dobrego wyjaśnienia. Zaktualizowałem odpowiedź, aby wyjaśnić, w jaki sposób zaakceptowana odpowiedź różni się od "array_diff". – treeface

+1

Wystarczy sprawdzić prosty przykład na http://php.net/manual/en/function.array-diff.php i działa (zachowuje się) zgodnie z oczekiwaniami (EDYCJA) dla prostych tablic. Ale rekurencja nie działa tak, jak powinna. :( – cottton

2

Spróbuj kod:

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

przykład:

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

wyjściowa:

array_diff_assoc_recursive($a,$b); 

Array 
(
    [product_a] => Array 
     (
      [color] => Red 
      [serial] => Array 
       (
        [2] => 3 
       )  
     )  
) 
Powiązane problemy