数组合并递归旧值与新值
Posted
技术标签:
【中文标题】数组合并递归旧值与新值【英文标题】:Array Merge Recursive old vs new values 【发布时间】:2021-10-22 11:45:52 【问题描述】:我想合并两个数组来比较新旧值。例如,$arr1
是旧值 $arr2
是新值。
如果数据被删除 $arr2 是一个空数组。示例:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [];
为此,我在合并后的预期输出是
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => null
],
"name" => [
'old' => "DB1",
'new' => null
],
"slug" => [
'old' => "db1",
'new' => null
],
"url" => [
'old' => "https://www.db1.org",
'new' => null
],
]
]
];
如果 arr2
不同,则值应出现在 new
字段中,而不是 null。
例如:
$arr1 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
$arr2 = [
"databases" => [
0 => [
"id" => 5
"name" => "DB2"
"slug" => "db2"
"url" => "https://www.db2.com"
]
]
];
预期输出:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => 1,
'new' => 5
],
"name" => [
'old' => "DB1",
'new' => "DB2"
],
"slug" => [
'old' => "db1",
'new' => "db2"
],
"url" => [
'old' => "https://www.db1.org",
'new' => "https://www.db2.com"
],
]
]
];
情况 3 是 $arr1 为空但 $arr2 已填充:
$arr1 = [];
$arr2 = [
"databases" => [
0 => [
"id" => 1
"name" => "DB1"
"slug" => "db1"
"url" => "https://www.db1.org"
]
]
];
预期的输出是:
$merged = [
"databases" => [
0 => [
"id" => [
'old' => null,
'new' => 1
],
"name" => [
'old' => null,
'new' => "DB1"
],
"slug" => [
'old' => null,
'new' => "db1"
],
"url" => [
'old' => null,
'new' => "https://www.db1.org"
],
]
]
];
内置的 php 函数无法格式化 old
和 new
格式的数据,所以想知道如何解决这个问题?任何解决方案/建议将不胜感激。
更新
这是我之前尝试过的:
-
我尝试过简单的
array_merge_recursive
,但它不存储源数组。因此,如果您没有 $arr1
键,则最终合并的数组将只有一个值。
我尝试了一些更多的递归函数深夜但失败了,所以基本上没有任何东西可以显示我尝试过的东西。但是,今天早上,我想出了解决方案并将其发布为答案,以防有人需要使用它。
【问题讨论】:
嗨!目前,这个问题读起来有点像希望有人为你做你的工作。如果您自己尝试一下,并展示您想出的代码以及您认为哪里出了问题,您可能会得到更好的帮助。 @IMSoP 抱歉,我放弃并发布此内容时已是深夜。现在我又读了一遍,是的,听起来确实如此。我已经想出了一个解决方案,并将其作为答案发布,以防其他人需要这个。我只需要再测试一下。 注册。 “怎么办?”:为什么不设置两个数组,一个用于old,另一个用于new。然后获取密钥并进行各种操作。+
数组运算符可能会派上用场。只是一些想法。
【参考方案1】:
有趣的问题,只要一侧的(非空)数组意味着要遍历它,并且任何 skalar 或 null 都是终止节点(而如果任何旧的或新的数组将强制遍历更深,所以丢弃另一个非数组值):
它的工作原理是递归地在一个数组上映射旧的和新的,当决定遍历以提供null
值时,以防在迭代两者的键的超集时键成员不可用null
表示没有键:
$keys = array_unique(array_merge(array_keys($old ?? []), array_keys($new ?? [])));
$merged = [];
foreach ($keys as $key)
$merged['old'] = $old[$key] ?? null;
$merged['new'] = $new[$key] ?? null;
然后可以递归应用,我发现将$old
和$new
处理为['old' => $old, 'new' => $new]
更容易,因为可以递归合并相同的结构:
function old_and_new(array $old = null, array $new = null): array
$pair = get_defined_vars();
$map =
static fn(callable $map, array $arrays): array => in_array(true, array_map('is_array', $arrays), true)
&& ($parameter = array_combine($k = array_keys($arrays), $k))
&& ($keys = array_keys(array_flip(array_merge(...array_values(array_map('array_keys', array_filter($arrays, 'is_array'))))))
)
? array_map(
static fn($key) => $map($map, array_map(static fn($p) => $arrays[$p][$key] ?? null, $parameter)),
array_combine($keys, $keys)
)
: $arrays;
return $map($map, $pair);
print_r(old_and_new(new: $arr2));
在线演示:https://3v4l.org/4KdLs#v8.0.9
内部在技术上适用于两个以上的数组,例如三。它向上“移动”数组键,类似于转置操作。顺便说一句。有一个类似的问题(但只是类似的,对于您的问题中有趣的部分,它没有得到回答,我的回答在这里并不直接适用):
Transposing multidimensional arrays in PHP【讨论】:
感谢您的解决方案。但如果数组的深度不同,这将失败。因此,如果“数据库”的级别嵌套更深一层,它将失败。 @Coola:是的,这是该建议的一个限制,因为必须映射遍历(按深度)才能将其映射。我现在把它变成了一个递归函数,它实际上更容易了,但是我没有太多时间来写描述,但你已经可以找到更新的答案了。 谢谢哈克雷。另外它与我下面的答案相比如何?两者都有什么优势吗? 它做不同的事情:把 old+new 变成一个数组 first 然后遍历它。构建唯一键组(您的array_keys($old) + array_keys($new)
可能会看到您不希望键的副作用),允许严格不需要中间体(旧、新 1、新 2、新 3、最新),但它概括了可以提供稳定性的代码.直接比较,通过使用一个而不是两个(或三个等)参数来简化递归。否则:这个示例代码风格真的不是很可读。它应该有一个版本,可以用 foreach 等显示它并进行比较。【参考方案2】:
在查看我自己的代码后,这是我想出的解决方案。我在这里发布它以防其他人需要解决方案:
/**
* Function to merge old and new values to create one array with all entries
*
* @param array $old
* @param array $new
* @return void
*/
function recursiveMergeOldNew($old, $new)
$merged = array();
$array_keys = array_keys($old) + array_keys($new);
if($array_keys)
foreach($array_keys as $key)
$oldChildArray = [];
$newChildArray = [];
if(isset($old[$key]))
if(!is_array($old[$key]))
$merged[$key]['old'] = $old[$key];
else
$oldChildArray = $old[$key];
else
$merged[$key]['old'] = null;
if(isset($new[$key]))
if( !is_array($new[$key]))
$merged[$key]['new'] = $new[$key];
else
$newChildArray = $new[$key];
else
$merged[$key]['new'] = null;
if($oldChildArray || $newChildArray)
$merged[$key] = recursiveMergeOldNew($oldChildArray, $newChildArray);
return $merged;
注意 - 此解决方案需要测试。
【讨论】:
以上是关于数组合并递归旧值与新值的主要内容,如果未能解决你的问题,请参考以下文章
如果在更新触发之前未在 oracle 中进行更新,则新值与旧值相同