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

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

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

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

 Wrikken06 окт. 2010 г., 22:30
Этопросто для тестирования этих выходов временно или для более длительного использования? Это для теста, простойwdiff надvar_export вывод должен сделать свое дело ...
 user15184106 окт. 2010 г., 22:42
Во вложенной структуре, если один элемент является массивом из 6, а другой - массивом из 3, это сработаетwdiff? Потому что в выводе, скажем, из строк 0-30 он будет идентичен, а с конца обратно в строку 36 он будет идентичен. Это будут только те средние линии, которые будут отличаться - 3 против 6. Если wdiff посмотрит на это, это сработает?
 Wrikken06 окт. 2010 г., 23:03
Вывод не будет сильно разделен на пары ключ / значение, однако он будет стараться сопоставлять строки до и после для дальнейшего соответствия, и ИМХО, если я просто проверяю, это подойдет. Просто используйте простойтестовый скрипт здесь и посмотрим, достаточно ли это хорошо для вашей цели. Альтернативой является рекурсивная функция, не такая уж сложная, но более трудоемкая.

Ответы на вопрос(5)

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
                )    
        )    
)

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

$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

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

Есть две проблемы, которые в основном связаны с соответствием ключей:

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 создаст пустой массив.

Если два массива имеют одинаковые значения, но разные ключи, он возвращает больше значений, чем ожидалось. Если у вас есть два массива$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;
}
 treeface25 окт. 2016 г., 01:14
@JeffPuckettII извините, я не дал хорошего объяснения. Я обновил ответ, чтобы объяснить, чем принятый ответ отличается отarray_diff.
 fractal524 июл. 2018 г., 21:20
Этот ответ работает отлично. Спасибо!
 cottton22 мая 2017 г., 18:25
Только что проверил простой пример наphp.net/manual/en/function.array-diff.php и он работает (ведет себя), как ожидалось (РЕДАКТИРОВАТЬ) для простых массивов. Но рекурсивный не работает так, как должен. :(
 Jeff Puckett24 окт. 2016 г., 22:14
Не могли бы вы объяснить, чем отличаются результаты? Для моих тестов я получаю точно такие же результаты. Спасибо
 Max Ivak03 авг. 2019 г., 08:58
это не работает, если у нас есть ассоциативные массивы со значениями в качестве объектов. Например,$a = [ "foo"=>(object)["p1"=>1, "p2"=>2], "moo"=>(object)["p3"=>3]], $b=[ "foo" => (object)["p4"=>4]].
 Max Ivak03 авг. 2019 г., 09:04
Иногда рассматривать [1, 2] и [2,1] по-разному не является желанным желанием. Массивы не являются множествами, где порядок элементов не важен. Массив PHP может представлять многие вещи. Нелегко отличить ассоциативный массив (с ключами любого типа) от обычного массива (с целочисленными ключами). Регулярный массив[1,2] считается отличным от массива[2,1], Вот почему у нас есть функции для добавления элементов в начало или конец массива, а не просто функция для добавления элемента в массив (set). Если вы хотите обрабатывать массив как набор, тогда это будет другая функция для сравнения массива как набора.
Решение Вопроса

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

 user15184110 авг. 2012 г., 17:10
@JonL. Я думаю, что он должен быть автором вопроса, чтобы сделать это :)
 Göktuğ Öztürk16 авг. 2018 г., 15:41
Чтобы эта функция была совместима с array_diff_assoc;if ($mValue != $aArray2[$mKey]) { линия должна бытьif ((string)$mValue !== (string)$aArray2[$mKey]) { как задокументировано в php.net. В противном случае эта функция возвращает неправильное значение для$a = ['c' => false]; $b = ['c' => 0];
 KoalaBear06 июл. 2017 г., 11:58
Строка значения сравнения должна быть сif ($mValue !== $aArray2[$mKey]) {Таким образом, он также обнаружит его при изменении типа значения.
 frops17 сент. 2013 г., 14:56
Спасибо) Хорошая идея.
 Jeff Puckett24 окт. 2016 г., 22:12
@CommaToast Можете ли вы привести пример, когда он возвращает данные, которые не изменились? Для моих тестов пара только возвращает разницу.
 Jay Milagroso13 мая 2011 г., 10:07
оно работает. Спасибо за это.
 Jon L.20 апр. 2012 г., 08:04
@Zend_Sklave, так как ответ mhitza сработал для вас, вы должны пометить его как фактически отвечающий на ваш запрос ...
 treeface01 нояб. 2016 г., 20:38
Пожалуйста, смотрите мой ответ для обновленной версии этой функции, которая соответствует какarray_diff работает (плюс объяснение, почему этот ответ не работает).
 cottton22 мая 2017 г., 21:20
это ведет себя больше какarray_diff_assoc (и это хорошо)
 klipach01 окт. 2018 г., 16:13
Этот пример на самом деле не работает. Легко проверить$a = [1,2,3,4]; $b = [4,5,6,7]; arrayRecursiveDiff($a, $b);
 CommaToast11 нояб. 2014 г., 03:06
Эта функция возвращает много данных, которые не изменились вообще. Я не понимаю, как это полезно.
 Fabian Becker02 авг. 2016 г., 08:37
Кроме того, если вы действительно хотите получить разницу между двумя массивами, вам нужно запустить эту функциюдважды чтобы не только найти то, что является частью массива 1 и не частью массива 2, но также и то, что является частью массива 2, но не частью массива 1. Может показаться очевидным для некоторых ... но не для других (таких как я сначала;) )

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

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

Ваш ответ на вопрос