Рекурсивный array_diff()?
Я ищу некоторый инструмент, чтобы дать мне рекурсивный diff двух массивов. То, что я представляю, представляет собой веб-страницу с двумя цветными древовидными структурами. На каждом дереве зеленые - это части массива, которые совпадают в обоих массивах, а красный - для частей каждого, которые не соответствуют другому. Что-то вроде вывода dBug
У меня есть код, который дает мне вложенный массив для заполнения отчета. Я разрабатываю новый метод, который должен быть быстрее, но мне нужно проверить значения, а также структуру, чтобы убедиться, что она дает результат, идентичный старому.
Есть ли что-то, что я могу использовать? Или мне нужно написать это? Или есть другой способ достичь моих целей?
Ответы
Ответ 1
Существует одна такая функция, реализованная в комментариях 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 или более массивов за раз. Также этот метод использует проверку ключей и выполняет свободную проверку.
Ответ 2
Принятый ответ близок к правилу, но он действительно не эмулирует array_diff
правильно.
Есть две проблемы, которые в значительной степени вращаются вокруг соответствия ключей:
-
array_diff
имеет конкретное поведение, когда оно не приводит к результату для ключа массива, который полностью отсутствует во втором массиве, если его значение все еще находится во втором массиве. Если у вас есть два массива $first = ['foo' => 2, 'moo' => 2]
и $second = ['foo' => 2]
, используя принятую функцию ответа, выход будет ['moo' => 2]
. Если вы запускаете те же массивы с помощью array_diff
, он будет создавать пустой массив. Это связано с тем, что вышеупомянутая функция final 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;
}
Ответ 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
)
)
)
Ответ 4
Попробуйте этот код:
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);
Ответ 5
Ответ Мохамада работает хорошо, за исключением того, что он нуждается в изменении на линии:
$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