递归和引用传递

问题描述 投票:0回答:5

我有一棵具有以下结构的类别树:

[6] => Array
    (
        [id] => 6
        [name] => computers
        [productCount] => 0
        [children] => Array
            (
                [91] => Array
                    (
                        [id] => 91
                        [name] => notebook
                        [productCount] => 5
                        [children] => Array
                            (
                            )
                    )

                [86] => Array
                    (
                        [id] => 86
                        [name] => desktop
                        [productCount] => 0
                        [children] => Array
                            (
                            )
                    )
            )
    )

除了子类别之外,每个类别还可能包含产品(就像文件夹可能包含子文件夹和仅文件)。

我正在尝试编写一个递归函数,我想将此数组作为参考,并删除 [productCount] = 0 的两个叶类别以及包含此类空节点的所有父类别。换句话说,处理后我只想拥有那些在任何子级别上包含产品的类别。

我已经编写了一些代码,现在正在调试它,它不会删除空节点。可能是我没有正确使用参考文献。如果可能的话,请帮我修复它。

    function pruneTree( & $node) {
    if ( ! $node['children'] && ! $node['productCount']) {
        unset($node);
    }
    if ( ! empty($node['children'])) {
        foreach ($node['children'] as $key => $child) {
            pruneTree($node['children'][$key]);
        }
    }
    return;
}
php recursion pass-by-reference
5个回答
6
投票

您还可以更改函数中的参数以采用节点数组而不是单个节点。这会稍微改变递归,并防止需要传递密钥:

function pruneTree(&$nodes) {
    foreach ($nodes as $key => $node) {
        if (!$node['children'] && !$node['productCount']) {
            unset($nodes[$key]);
        } elseif (!empty($node['children'])) {
            pruneTree($nodes[$key]['children']);
            // This line checks if all the children have been pruned away:
            if (empty($nodes[$key]['children'])) {
                unset($nodes[$key]);
            }
        }
    }
}

此外,还添加了一项检查,确保如果所有子节点都被修剪,则父节点(现在为叶节点)也会被修剪。

希望这有帮助!


测试数据:

$data = array(
    6 => array(
        'id' => 6,
        'name' => 'computers',
        'productCount' => 0,
        'children' => array(
            91 => array(
                'id' => 91,
                'name' => 'notebook',
                'productCount' => 5,
                'children' => array()
            ),
            86 => array(
                'id' => 86,
                'name' => 'desktop',
                'productCount' => 0,
                'children' => array()
            )
        )
    )
);

电话:

pruneTree($data);
echo '<pre>';
print_r($data);
echo '</pre>';

6
投票

unset
仅删除引用,但不删除引用的变量:

如果通过引用传递的变量

unset()
位于函数内部,则只有局部变量会被销毁。调用环境中的变量将保留与调用
unset()
之前相同的值。

因此您需要传递父数组和键来删除该变量:

function pruneTree(&$parent, $key) {
    $node = &$parent[$key];
    if (!$node['children'] && !$node['productCount']) {
        unset($parent[$key]);
    }
    if (!empty($node['children'])) {
        foreach ($node['children'] as $key => &$child) {
            pruneTree($node['children'], $key);
        }
    }
}

2
投票

我会那么做。注意 foreach 中的“&”。

function pruneTree(&$node)
{
    foreach ($node as $index => &$value) {
        if (empty($value)) {
            unset($node[$index]);
        } elseif (is_array($value)) {
            pruneTree($value);
        }
    }
}

1
投票

我不知道是否是这种情况,但是当我需要在数组中递归更改值时,我也需要将 & 传递给 foreach 值。

private function convertXMLPart(&$array) {
        foreach ($array as $rowKey => &$row) {
            if (gettype($row) != 'string') {
                $row = (array)$row;
                if (!empty($row['@attributes'])) {
                    foreach ($row['@attributes'] as $key => $value) {
                        $row[$key] = $value;
                    }
                    unset($row['@attributes']);
                    $array[$rowKey] = $row;
                }
                $this->convertXMLPart($row);
            }
        }
    }

0
投票

我也在寻找这个案例,不知何故,我刚才偶然发现了这个线程。并发现使用

unset
是一个错误。 F! PHP。和我在C/C++中使用的方式不一样

这是我的解决方案,根本不使用函数

unset
,我已经尝试过并且成功了。

我知道Gumbo的解决方案是正确的,这是旧线程,但我只是想分享一些不同的想法,尚未在这里共享并且更易于阅读。

我的想法是:

  1. 声明新分支/叶子节点数组来存储新节点。
  2. 执行后序树遍历
  3. 在节点的操作中,将条件插入到需要保留的节点,而不是要删除的节点,因为
    unset
    可能会误导您。将其存储到声明的数组中。
  4. 将旧列表替换为函数参数中的新列表。
    function pruneTree(& $tree) {

        $new_tree = [];  // Declare to rebuild the nodes

        foreach($tree as $node) {  // Traverse the branch

            if(!empty($node["children"])) { // Visit the children branch first in postorder
                pruneTree($node["children"]);
            }

            /* Node's action */
            if(!empty($node["children"]) || $node["productCount"] > 0) {
                $new_tree [] = $node;
            }
            /* End of node's action */

        }

        $tree = $new_tree ; // Replace the tree with their new filtered children
    }
© www.soinside.com 2019 - 2024. All rights reserved.