【问题标题】:(PHP) Aggregate counting of children in multidimensional array being populated in recursive function(PHP)在递归函数中填充多维数组中的子项的聚合计数
【发布时间】:2015-01-12 15:30:47
【问题描述】:

我在 MySQL 数据库中有一个表示遗传语言树的表。每行是一种语言,带有一个 ID 和一个父 ID;例如:

id   | language            | parent
-----+---------------------+--------
1    | Proto-Indo-European | NULL
6    | Celtic              | 1
8    | Insular Celtic      | 6
9    | Goidelic            | 8
14   | Irish               | 9
16   | Manx                | 9
21   | British             | 8
22   | Welsh               | 21
109  | Germanic            | 1
115  | West Germanic       | 109
117  | Anglo-Saxon         | 115
118  | Anglic              | 117
119  | Old English         | 118

我的目标是使用简单的递归 PHP 函数将这些行变成一个漂亮的嵌套 HTML 列表,在每种语言之后有聚合计数器,从而产生类似这样的结果(基于上面示例行中的语言):

  • 原始印欧语 (11)
    • 凯尔特人 (6)
      • 岛屿凯尔特人 (5)
        • 戈德利克 (2)
          • 爱尔兰语
          • 马恩岛
        • 英式 (1)
          • 威尔士语
    • 日耳曼语 (4)
      • 西日耳曼语 (3)
        • 盎格鲁-撒克逊 (2)
          • 英语 (1)
            • 古英语

我有以下 PHP 函数(此处简化),它创建了一个具有正确结构的多维数组:

function select_languages_hierarchical($parent = NULL) {
    global $db;
    $branch = array();
    $query = "SELECT * FROM languages WHERE parent = $parent";

    if ($q = $db->query($query)) {
        while ($row = $q->fetch_assoc()) {
            $element['id'] = $row['id'];
            $element['name'] = $row['language'];
            $children = select_languages_hierarchical($row['id']);

            if ($children) {
                $element['children'] = $children;
            }

            $branch[] = $element;
        }
    }
    return $branch;
}

这会生成一个与上面的嵌套列表匹配的数组,其中嵌套数组位于每个数组的 children 元素中。

但是,尽管在 SO 上绞尽脑汁、在谷歌上搜索并查看了大量的递归、聚合计数和数组计数问题,但我终其一生都无法找到创建计数器的方法描述每种语言的后代语言数量。

无论我做什么,无论我在哪里创建、修改和使用我的计数器变量,以及我尝试计数的任何事情(无论是每次迭代语言时执行一次ˋ$count++ˋ,执行一次ˋcount($children)ˋ等),当函数达到“更高”级别时,我总是会得到计数器未重置的结果,因此我会得到这样的列表:

  • 原始印欧语 (12)
    • 凯尔特人 (6)
      • 岛屿凯尔特人 (5)
        • 戈德利克 (2)
          • 爱尔兰语
          • 马恩岛
        • 英式 (4)
          • 威尔士语
    • 日耳曼语 (9)
      • 西日耳曼语 (12)
        • 盎格鲁-撒克逊 (14)
          • 英语 (15)
            • 古英语

——或者类似的东西——数字和它们加起来的方式在我尝试的各种不同的实现中有所不同;但它们都有一个共同点,即计数器一直在增加,而从未被重置。或者,如果我确实尝试在某个时候重置它,它自然会在 每次 迭代时重置,给我留下:

  • 原始印欧语 (2)
    • 凯尔特人 (2)
      • 岛屿凯尔特人 (1)
        • 戈德利克 (2)
          • 爱尔兰语
          • 马恩岛
        • 英式 (1)
          • 威尔士语
    • 日耳曼语 (1)
      • 西日耳曼语 (1)
        • 盎格鲁-撒克逊 (1)
          • 英语 (1)
            • 古英语

显然,逻辑不是我的强项。

有人可以通过建议一种方法来帮助我实现我正在寻找的那种“智能计数”,从而帮助我的大脑避免自发燃烧和/或内爆吗?

【问题讨论】:

  • 你已经接近了。基本上更新您的数组,以便您在每个节点存储子计数和子信息。例如$node['count'] = 3, $node['name'] = 'West Germanic', $node['children'] = array of child languages.
  • @MarcB 这正是我想要做的。问题在于获取正确的数字(即您的示例中的 3)以存储在 $element['countchildren'] 中(正如我在代码中一直调用的那样)。
  • 当你进行递归调用时。基本上用您从递归调用收到的节点中存储的计数更新当前节点的子计数,例如$curnode['count'] += $children['count']。问题是现在你只是在做$element['children'] = $children,它将取代你以前的递归调用。您需要合并结果。
  • @MarcB 恐怕我不太关注你。您能否在适当的位置发表您的评论,以便我准确地更新具有哪些值的节点?
  • 投反对票的人是否愿意解释他们的投反对票(如果他们看到这个)?

标签: php recursion multidimensional-array count children


【解决方案1】:

考虑一下,您可以只做一个基本的select * 并在 PHP 中构建您的树,然后在那里进行计数,而不是在 PHP 中运行无数的递归查询。当然,这仅在您想要整个语言树时才有用。

$lang = array();
$sql = "SELECT * FROM languages";
... run query ...
while($row = fetch results) {
     // store this lang's node
     $lang[$row['id']]['name'] = $row['name']; 

     // add this language to its parent's child array
     $lang[$row['parent']]['children'][$row['id']] = $row['id'];

     // increment its parent's counter
     $lang[$row['parent']]['count']++;
}

现在,这可能会发出一堆关于未定义数组和诸如此类的警告,因为在尝试更新它的计数之前我没有测试父级的存在。但这只是一些基本的if (!isset()) { initialize node } 类型的东西。

输出到嵌套的<ul> 将是一个递归函数,但由于您现在已经在树中获得了子节点计数,所以它会简单得多:

function output($id = 1) { // assuming "1" is your tree root node
   echo '<li>' . $lang[$id]['name'] . '(' . $lang[$id]['count'] . ')';
   if ($lang[$id]['count'] > 0) {
       echo '<ul>';
       foreach($lang[$id]['children'] as $childID) {
            output($childID);
       }
       echo '</ul>';
   }
   echo '</li>';
}

【讨论】:

  • 这很整洁,很好的横向思维!我曾经玩弄过完全用 PHP 构建树的想法(节省大量的 MySQL 查询总是一个好主意),但想不出一个好的、干净的方法来做到这一点。如果我能让它按预期工作,我会试试这个并接受。 :-)
猜你喜欢
  • 2017-01-25
  • 2015-06-29
  • 2014-10-31
  • 1970-01-01
  • 2016-12-06
  • 1970-01-01
  • 2016-07-13
  • 2012-04-07
  • 2017-02-26
相关资源
最近更新 更多