2010-10-06 4 views
25

Я ищу некоторый инструмент, чтобы дать мне рекурсивный diff двух массивов. То, что я представляю, представляет собой веб-страницу с двумя цветными древовидными структурами. На каждом дереве зеленые - это части массива, которые совпадают в обоих массивах, а красный - для частей каждого, которые не соответствуют другому. Что-то вроде вывода dBugрекурсивный массив_дифф()?

У меня есть код, который дает мне вложенный массив для заполнения отчета. Я разрабатываю новый метод, который должен быть быстрее, но мне нужно проверить значения, а также структуру, чтобы убедиться, что она дает результат, идентичный старому.

Есть что-то там, что я могу использовать? Или мне нужно написать это? Или есть другой способ достичь моих целей?

+0

ли это _just_ для тестирования этих выходов временно, или для более длительного использования? Это для теста, простой вывод 'wdiff' над' var_export' должен делать трюк ... – Wrikken

+0

Во вложенной структуре, если один элемент представляет собой массив из 6, а другой - массив из 3, будет эта поездка up 'wdiff'? Потому что в выводе, скажем, из строк 0-30, он будет идентичным, а с конца до строки 36 он будет идентичным. Только те средние линии будут разными - 3 против 6. Если wdiff посмотрит на это, сработает ли он? – user151841

+0

Выход не будет довольно разделен на пары ключ/значение, однако он будет стараться сопоставлять строки до и после для дальнейшего совпадения, а IMHO, если я просто не могу проверить, это будет сделано. Просто используйте простой [testcript здесь] (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; 
} 

Реализация обрабатывает только два массива в то время, но я не думаю, что на самом деле обладают проблемы. Вы можете запустить diff последовательно, если вам нужно разделить 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 , и он работает (ведет себя) как ожидалось (EDIT) для простых массивов. Но рекурсивный не работает должным образом. :( – 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 
       )  
     )  
)