具有相同键和值的php多维数组合并
我花了几个小时试图找到这个问题的答案,但我正在努力。我对PHP和各种内置函数相当熟悉,可以构建一个复杂的foreach()循环来实现这一点,但我想问一下是否有人有更智能的解决方案来解决我的问题 我有以下三行的简化示例数组(实际数组通常更大、更复杂,但问题是相同的) 我想得到的是以下输出:具有相同键和值的php多维数组合并,php,arrays,Php,Arrays,我花了几个小时试图找到这个问题的答案,但我正在努力。我对PHP和各种内置函数相当熟悉,可以构建一个复杂的foreach()循环来实现这一点,但我想问一下是否有人有更智能的解决方案来解决我的问题 我有以下三行的简化示例数组(实际数组通常更大、更复杂,但问题是相同的) 我想得到的是以下输出: [[ "widget_id" => "widget1", "size" => "large",
[[
"widget_id" => "widget1",
"size" => "large",
"item" => [
"item_id" => "item1",
"shape" => "circle",
"paint" => [[
"paint_id" => "paint1",
"colour" => "red",
],[
"paint_id" => "paint2",
"colour" => "green",
]]
]
],[
"widget_id" => "widget2",
"size" => "medium",
"item" => [
"item_id" => "item1",
"shape" => "circle",
"paint" => [
"paint_id" => "paint1",
"colour" => "red",
]
]
]]
基本上,当两行共享相同的键和值时,合并它们。当键相同,但值不同时,保留这两个值并将它们放在键下的数字数组中(有点像array\u merge\u recursive
所做的那样)
挑战在于,值本身可以是数组,并且存在未知数量的级别。有没有一种聪明而有效的方法可以做到这一点,或者我必须求助于一个繁重的foreach
循环
谢谢你的浏览,希望有比我更聪明的人在读这篇文章 我通过下面的函数获得了预期的数组结构,我希望注释能明确说明其中的内容:
function complex\u merge(数组$arr):数组
{
//分组项目
$result=[];
$iterationKey=0;
//循环浏览每一项
while(($element=array\u shift($arr))!==null){
//按原样保存标量值
$scalarValues=array_filter($element'is_scalar');
//在数组中保存数组值
$arrayValues=数组映射(fn(数组$arrVal)=>[$arrVal],数组过滤器($element,'is_数组'));
$arrayValuesKeys=数组_键($arrayValues);
$result[$iterationKey]=array\u merge($scalarValues,$arrayValues);
//与其他项目进行比较
对于($i=0;$i
只需将$rows
传递给函数,快速检查值:
echo“”。打印(复杂合并($rows),true)。“”;
/*
显示:
排列
(
[0]=>阵列
(
[widget_id]=>widget1
[大小]=>大
[项目]=>阵列
(
[项目id]=>项目1
[形状]=>圆形
[绘制]=>阵列
(
[0]=>阵列
(
[绘制id]=>绘制1
[颜色]=>红色
)
[1] =>阵列
(
[paint_id]=>paint2
[颜色]=>绿色
)
)
)
)
[1] =>阵列
(
[widget_id]=>widget2
[大小]=>中等
[项目]=>阵列
(
[项目id]=>项目1
[形状]=>圆形
[绘制]=>阵列
(
[绘制id]=>绘制1
[颜色]=>红色
)
)
)
)
*/
以下是我自己的尝试。我想我更喜欢AymDev的版本,更简洁。我想知道哪个更快
$array = ComplexMerge::normalise($array);
用法:
到目前为止您尝试了什么?如果您有多个级别,则可能需要递归function@FelippeDuarte我正在编写一个递归的
foreach()
函数,因为我怀疑没有任何“智能”解决方案(请证明我错了!)。我一做完就发。不会很漂亮的!它可以有更好的var名称,也可能会被优化,但它很有效,我做得很开心:)谢谢你提供了这个很好的例子。我也发布了我自己的尝试,但我认为你的更好。我做了一个快速的基准测试,我的似乎快了约0.01毫秒。但是面向对象的方法更容易使用:)谢谢。实际上,我从你的方法中学到了很多,你使用了一些我不熟悉(或不习惯)的内置方法。也许我会玩一个混合版本,当我有时间,现在我已经实现了你的版本,因为它更短!再次感谢你的帮助。
class ComplexMerge{
/**
* Checks to see whether an array has sequential numerical keys (only),
* starting from 0 to n, where n is the array count minus one.
*
* @link https://codereview.stackexchange.com/questions/201/is-numeric-array-is-missing/204
*
* @param $arr
*
* @return bool
*/
private static function isNumericArray($arr)
{
if(!is_array($arr)){
return false;
}
return array_keys($arr) === range(0, (count($arr) - 1));
}
/**
* Given an array, separate out
* array values that themselves are arrays
* and those that are not.
*
* @param array $array
*
* @return array[]
*/
private static function separateOutArrayValues(array $array): array
{
$valuesThatAreArrays = [];
$valuesThatAreNotArrays = [];
foreach($array as $key => $val){
if(is_array($val)){
$valuesThatAreArrays[$key] = $val;
} else {
$valuesThatAreNotArrays[$key] = $val;
}
}
return [$valuesThatAreArrays, $valuesThatAreNotArrays];
}
/**
* Groups row keys together that have the same non-array values.
* If every row is already unique, returns NULL.
*
* @param $array
*
* @return array|null
*/
private static function groupRowKeysWithSameNonArrayValues($array): ?array
{
foreach($array as $key => $row){
# Separate out the values that are arrays and those that are not
[$a, $v] = self::separateOutArrayValues($row);
# Serialise the values that are not arrays and create a unique ID from them
$uniqueRowId = md5(serialize($v));
# Store all the original array keys under the unique ID
$deduplicatedArray[$uniqueRowId][] = $key;
}
# If every row is unique, there are no more rows to combine, and our work is done
if(!$a && count($array) == count($deduplicatedArray)){
return NULL;
}
return $deduplicatedArray;
}
private static function mergeRows(array $array): array
{
# Get the grouped row keys
if(!$groupedRowKeys = self::groupRowKeysWithSameNonArrayValues($array)){
//If there are no more rows to merge
return $array;
}
foreach($groupedRowKeys as $uniqueRowId => $keys){
foreach($keys as $id => $key){
# Separate out the values that are arrays and those that are not
[$valuesThatAreArrays, $valuesThatAreNotArrays] = self::separateOutArrayValues($array[$key]);
//We're using the key from the grouped row keys array, but using it on the original array
# If this is the first row from the group, throw in the non-array values
if(!$id){
$unique[$uniqueRowId] = $valuesThatAreNotArrays;
}
# For each of the values that are arrays include them back in
foreach($valuesThatAreArrays as $k => $childArray){
$unique[$uniqueRowId][$k][] = $childArray;
//Wrap them in a numerical key array so that only children and siblings are have the same parent-child relationship
}
}
}
# Go deeper
foreach($unique as $key => $val){
foreach($val as $k => $valuesThatAreNotArrays){
if(self::isNumericArray($valuesThatAreNotArrays)){
$unique[$key][$k] = self::mergeRows($unique[$key][$k]);
}
}
}
# No need to include the unique row IDs
return array_values($unique);
}
public static function normalise($array): ?array
{
$array = self::mergeRows($array);
return $array;
}
}
$array = ComplexMerge::normalise($array);