【问题标题】:A recursive walk-through over a 10000 element array in PHP: Maximum function nesting level reached递归遍历 PHP 中 10000 个元素的数组:达到最大函数嵌套级别
【发布时间】:2020-09-07 09:35:05
【问题描述】:

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

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

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

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

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

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

问题是我有数以万计的内部数组,PHP 抛出“PHP 致命错误:未捕获的错误:达到 '256' 的最大函数嵌套级别,正在中止!”。

在仔细检查代码并运行调试会话后,我发现 $arr 确实减少了每次调用的大小,所以我认为问题仅在于最大函数嵌套设置。然而,在我看来,将此设置更改为 100 万左右是不切实际的。那么,我们还有什么其他的解决方案呢?

【问题讨论】:

  • 此数据是否来自数据库?也许您可以在原点进行操作。

标签: php arrays recursion


【解决方案1】:

除非是要求,否则递归可能不是这里的方法。

<?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 中的每个元素一次,并且哈希查找(又名isset($v['id']))是一个O(1) 操作。正如 Oliver 的解决方案所指出的那样,您实际上不需要检查密钥是否存在,您可以盲目地 += 到该密钥。

【讨论】:

  • 我正在写一个完全一样的解决方案。
  • @ÉderRochaBezerra 数据库是一个很好的问题,你可以在那里剪掉很多这样的工作。
  • 此解决方案并不能完全满足 OP 的需求(如果您查看 OP 的目标)
【解决方案2】:

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

// 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 目前不支持.

【讨论】:

    【解决方案3】:

    此解决方案基于您的数组名为$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>';
    

    【讨论】:

      【解决方案4】:

      只使用一个读取循环并同时添加和创建新数组很简单。最后使用 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));    
      

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2013-08-02
          • 2011-07-03
          • 2017-08-06
          • 2013-07-03
          相关资源
          最近更新 更多