рекурсивный array_diff ()? - PullRequest
       23

рекурсивный array_diff ()?

30 голосов
/ 07 октября 2010

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

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

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

Ответы [ 5 ]

57 голосов
/ 07 октября 2010

В комментариях 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 последовательно, если вам нужен diff из 3 или более массивов одновременно. Также этот метод использует проверку ключей и выполняет свободную проверку.

9 голосов
/ 09 апреля 2015

Принятый ответ близок к правильному, но на самом деле он не эмулирует 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;
}
4 голосов
/ 28 апреля 2016
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
                )    
        )    
)
2 голосов
/ 30 ноября 2015

Попробуйте этот код:

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);
0 голосов
/ 18 декабря 2018

Ответ Мохамад работает хорошо, за исключением того, что ему нужно изменить в строке:

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

с:

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

или, если вы используете Laravel, с:

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

В противном случае вы получите ошибки типа

Ошибка PHP: неопределенный индекс: some_key

когда some_key существует в $ secondArray, но не в $ firstArray

...