繁体   English   中英

递归遍历 PHP 中的 10000 个元素数组:达到最大 function 嵌套级别

[英]A recursive walk-through over a 10000 element array in PHP: Maximum function nesting level reached

我有一个 2 元素 arrays 的排序数组,如下所示:

array(
  array('id' => 1734, 'count' => 14),
  array('id' => 1734, 'count' => 7),
  array('id' => 9374, 'count' => 5),
  array('id' => 9374, 'count' => 253)
);

该数组按内部 arrays 的“id”字段排序。 目标是将其减少到以下内容:

array(
  array('id' => 1734, 'count' => 21),
  array('id' => 9374', 'count' => 258)
);

即对每个唯一的“id”的“count”元素进行聚合/求和。

我通过 function myfunc(array $arr){...} 执行 array_shift($arr) 然后调用 myfunc($arr) 中的数组 go。 它还对递归的基本情况(一个空数组)进行了适当的检查,并且应该在此时终止。

问题是我有成千上万的内部 arrays 和 PHP 抛出“PHP 致命错误:未捕获的错误:最大 ZC1C425268E68385D1AB5074C17A94F14'Z 嵌套级别达到。”

在仔细检查代码并运行调试 session 后,我看到 $arr 确实在每次调用中减小了它的大小,所以我认为问题只在于最大的 function 嵌套设置。 但是,对我来说,将此设置更改为 100 万似乎是不切实际的。 那么,我们还有什么其他的解决方案呢?

除非它是一个要求,否则递归可能不是 go 的方式。

<?php

$arr = array(
  array('id' => 1734, 'count' => 14),
  array('id' => 1734, 'count' => 7),
  array('id' => 9374, 'count' => 5),
  array('id' => 9374, 'count' => 253)
);

$agg = array();

foreach ($arr as $v){
  if (isset($agg[$v['id']])){$agg[$v['id']] += $v['count'];}
  else {$agg[$v['id']] = $v['count'];}
}

无论如何,您必须至少查看$arr中的每个元素一次,并且 hash 查找(又名isset($v['id']) )是 O(1) 操作。 正如奥利弗的解决方案所指出的那样,您实际上不需要检查密钥是否存在,您可以盲目地+=对该密钥。

使用迭代算法而不是递归算法:

// add up the count for each id
$counts = [];
foreach ($arr as $row) {
    $counts[$row['id']] += $row['count'];
}

// format the counts as an array of records
$answer = [];
foreach ($counts as $id => $count) {
    $answer[] = ['id' => $id, 'count' => $count];
}

请注意,递归算法在用于大型数据集时往往会超出堆栈的容量,除非它们以使用尾调用优化的方式编写,并且假设语言支持尾调用优化,PHP 目前不支持。

此解决方案基于您的数组称为$arr

$new_arr = [];

foreach($arr as $item) {
    $key = $item['id'];
    $new_arr[$key]['id'] = $item['id'];

    isset($new_arr[$key]['count']) ? 
    $new_arr[$key]['count'] += $item['count'] : 
    $new_arr[$key]['count'] = $item['count'];
}

//This code would output your expected result. Without array_values you will 
//get the key values equals to your values of id
echo '<pre>';
print_r(array_values($new_arr));
echo '</pre>';

只使用一个读取循环并同时添加和创建新数组很简单。 最后使用array_values()重新创建父数组键的顺序。

$arr = [
    ['id' => 1734, 'count' => 14],
    ['id' => 9388, 'count' => 25],
    ['id' => 1734, 'count' => 7],
    ['id' => 9374, 'count' => 5],
    ['id' => 9374, 'count' => 253]
];

$counts = [];

foreach($arr as $row)
{
    if(isset($counts[$row['id']])){
        $sum = $counts[$row['id']]["count"] + $row['count']];
        $counts[$row['id']] = [
            "id"    => $row['id'],
            "count" => $sum
        ];
    }else{
        $counts[$row['id']] = [
            "id"    => $row['id'],
            "count" => $row['count']
        ];
    }
}   



var_dump(array_values($counts));    

我希望这个提示会有所帮助。 代码经过测试,成功,代码不错!

谢谢你的回答。 我显然没有提到的一件事是我希望解决方案具有功能性。

我等效地(就结果而言)重写了算法以使用 array_reduce()。 这使我能够保持功能风格并完成工作。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM