【问题标题】:Converting a PHP array of key value pairs to a hierarchical nested tree structure将 PHP 键值对数组转换为分层嵌套树结构
【发布时间】:2014-07-09 13:06:51
【问题描述】:

我有一个由大约 6,000 个键/值对组成的数组结构。

数组的结构如下:

Array
(
[0] => Array
    (
        [parent] => parentA
        [name] => child1
    )

[1] => Array
    (
        [parent] => parentB
        [name] => childC
    )

[2] => Array
    (
        [parent] => parentA
        [name] => child2
    )

[3] => Array
    (
        [parent] => parentC
        [name] => child3
    )
[4] => Array
    (
        [parent] => child1
        [name] => child4
    )

[5] => Array
    (
        [parent] => child4
        [name] => child5
    )

我正在尝试从该数据源将输出按摩到

A) 我可以在以后的功能中使用的数组
B) 表格显示,其中每一行将是一个完整的链,每列将更深一层。本质上,如果您想到页面导航,这是一个面包屑显示,其中每个节点都将位于下一列中。

我在这里玩了一些方法

1) 在此堆栈溢出问题中使用递归函数:https://stackoverflow.com/a/2915920/997226,但是我无法修改它以使用我的数据,其中父级可以相同。在他们的 $tree 示例中,左侧(键)值始终是唯一的。

我知道在他们的示例中,他们的键是子项,值(右侧)是父项,但是我仍然无法让它为我工作,因为在我的数据中,两者都有多个相同的项目父方和子方。 (想想一篇文章可以包含在多个父类别中的复杂关系。

2) 我尝试开始创建唯一父元素的“基本数组”,然后创建一个递归函数来搜索“原始键值数组”,但这也不太奏效。

3) 我尝试将数据保存在数据库中,因为我非常熟悉使用左/右值作为嵌套集访问/操作数据,但我试图避免必须从一个数据库。

4) 我尝试使用各种 PHP 迭代器类,因为我已成功使用这些类来处理文件系统和构建文件/目录列表,所以我一直在使用 RecursiveArrayIterator / ParentIterator/ArrayIterator,但不能似乎想出了要使用的正确语法。

我知道对于这种大小的数据集,递归可能不如使用引用那么有效,但它似乎提供了最大的灵活性,我似乎无法让它正确地递归迭代。

除了这个特定的问题,我还试图更好地理解编程递归的算法性质。

我越是阅读其他人试图做类似事情的代码示例,但使用不同的数据结构,我就会变得更加困惑。

如果有人能帮助我指出正确的方向,我们将不胜感激。

澄清说明

  1. 会有多个级别。
  2. 有人指出,可以将数据结构视为有向无环图,这是完全合理的。

【问题讨论】:

  • 完全不清楚您要达到的目标。 A)你以后可以使用什么样的数组? B) 所以你有一个有向的非循环图,它有一个源和可能有许多接收器,你想提取从源到接收器的所有路径并显示它们?
  • Heuster,是的,谢谢,您对有向图的理解是正确的,并且希望可视化所有相关路径。

标签: php arrays algorithm recursion iterator


【解决方案1】:

** 更新 #2 - 我使用引用(不是递归)重新设计。这只需要一次通过数据。如果每个父项或子项尚不存在,则将其作为顶级项添加到数组(在本例中为 $a)。这个顶级项目的关键是父母或孩子的名字。该值是一个数组,其中包含对其子级顶级项的引用。此外,创建第二个数组 ($p),仅引用第一个数组 ($a) 中的父项。在一个非常快速的单遍中,所有的关系都被发现了。

代码(更新 #2):

<?php
$tree_base = array(
    array('parent' => 'parentA','name' => 'child1'),
    array('parent' => 'parentB','name' => 'childC'),
    array('parent' => 'parentA','name' => 'child2'),
    array('parent' => 'parentC','name' => 'child3'),
    array('parent' => 'child1','name' => 'child4'),
    array( 'parent' => 'child4', 'name' => 'child5'),
    array( 'parent' => 'DataSelect', 'name' => 'getBaseUri'),
    array( 'parent' => 'getBaseUri', 'name' => 'getKbBaseURI')
    );

    $tree = parseTree($tree_base);
    echo '<pre>'.print_r($tree, TRUE).'</pre>';
    showresults($tree);

function parseTree($tree){
    $a = array();
    foreach ($tree as $item){
        if (!isset($a[$item['name']])){
            // add child to array of all elements
            $a[$item['name']] = array();
        }
        if (!isset($a[$item['parent']])){
            // add parent to array of all elements
            $a[$item['parent']] = array();

            // add reference to master list of parents
            $p[$item['parent']] = &$a[$item['parent']];
        }
        if (!isset($a[$item['parent']][$item['name']])){
            // add reference to child for this parent
            $a[$item['parent']][$item['name']] = &$a[$item['name']];
        }   
    }
    return $p;
}

function showresults($tree, &$loop = array()){
        if (is_array($tree) & count($tree) > 0){       
                echo "<table>";
                echo "<tr>";
                foreach ($tree as $key => $children){
                    // prevent endless recursion
                    if (!array_key_exists($key, $loop)){
                        $loop[$key] = null;
                        echo "<tr>";
                        echo "<td style='border:1px solid'>";
                        echo $key;
                        echo "<td style='border:1px solid'>";
                        showresults($children, $loop);
                        echo "</td>";
                        echo "</td>";
                        echo "</tr>";
                    }
                }
                echo "</tr>";
                echo "</table>";
        }
}

?>

输出(更新#2):

Array
(
    [parentA] => Array
        (
            [child1] => Array
                (
                    [child4] => Array
                        (
                            [child5] => Array
                                (
                                )

                        )

                )

            [child2] => Array
                (
                )

        )

    [parentB] => Array
        (
            [childC] => Array
                (
                )

        )

    [parentC] => Array
        (
            [child3] => Array
                (
                )

        )

    [DataSelect] => Array
        (
            [getBaseUri] => Array
                (
                    [getKbBaseURI] => Array
                        (
                        )

                )

        )

)

**更新 #1 - 我已修复代码以显示多级子级(以及您的新示例数组结构)。为了保持干净,我只使用生成的数组元素的键来存储父项和子项的名称。表格输出在视觉上变得更加复杂。我为每个父/子组使用了一行,第一列为父组,第二列为其子组。这也是递归的,因此包含子项的列可以在相同格式的新表中显示其子项(如果有)(比解释更容易查看)。

输出(更新#1):

 Array
(
    [parentA] => Array
        (
            [child1] => Array
                (
                    [child4] => Array
                        (
                            [child5] => 
                        )

                )

            [child2] => 
        )

    [parentB] => Array
        (
            [childC] => 
        )

    [parentC] => Array
        (
            [child3] => 
        )

)

代码(更新#1):

<?php
$array = array(
    array('parent' => 'parentA',
                    'name' => 'child1'),
    array('parent' => 'parentB',
                    'name' => 'childC'),
    array('parent' => 'parentA',
                    'name' => 'child2'),
    array('parent' => 'parentC',
                    'name' => 'child3'),
    array('parent' => 'child1',
                    'name' => 'child4'),
    array( 'parent' => 'child4',
                    'name' => 'child5')
    );

    // parse array into a hierarchical tree structure
    $tree = parseTree($array);

    // Show results
    echo '<pre>';
    print_r($tree);
    echo '</pre>';  
    echo "<br>Table Format:";

    showresults($tree);

    function parseTree(& $tree, $root = null) {
        $return = null;
        // Traverse the tree and search for children of current parent
      foreach ($tree as $key=> $item){
      // A child is found
            if ($item['parent'] == $root){
                // append child into array of children & recurse for children of children
                $return[$item['name']] = parseTree($tree, $item['name']);
                // delete child so won't include again
                unset ($tree[$key]);
            }
            elseif ($root == null) {
                // top level parent - add to array 
                $return[$item['parent']] = parseTree($tree, $item['parent']);
                // delete child so won't include again
                unset ($tree[$key]);
            }
        }
        return $return;
    }

    function showresults($tree){

        if (is_array($tree)){       
            echo "<table>";
            echo "<tr>";

            foreach ($tree as $key => $children){
                echo "<tr>";

                echo "<td style='border:1px solid'>";
                echo $key;

                echo "<td style='border:1px solid'>";
                showresults($children, true);
                echo "</td>";

                echo "</td>";

                echo "</tr>";
            }

            echo "</tr>";
            echo "</table>";
        }
    }

?>

【讨论】:

  • mseifert,感谢您的代码示例。当我尝试这个时,它只显示一个父母及其相关的孩子。源数据将具有多个子级的子级,所有这些都在该父节点(键/值)结构中。所以我希望应该有一些是 Parent->Child->Child of Child->Child of Child of Child 类型的结果,我在示例 1 中没有看到。如果我不清楚,我很抱歉,我只是错过了一些非常基本的东西吗?
  • 我已经更新了上面的代码以显示 Child of Child 递归处理。希望这会有所帮助。
  • 我一直在这里尝试您的示例,当我使用 $array 示例运行它时,它看起来很完美。但是,当我在包含 6,000 个元素的数组上运行它时,它不会运行。我将它调整为只运行几百个并且它运行,但是数组和表格打印出来的看起来不像预期的那样。但是,这可能是因为当我将其限制为 400 时,没有足够的数据来拥有父/子。所以我的问题是 1)如果有一个键值并且没有该名称的其他父级,它应该创建一个顶级条目对吗?那么我的第二个问题是我应该增加服务器大小并再试一次吗?
  • 我想知道传递一个 $root_parent 变量是否有意义,这将是我想要查看的树的根?因此,与其尝试追踪整个树/图,不如只关注一个起点?
  • 我不肯定“不运行”和“增加服务器大小”的确切含义。对于 6000 个元素,可能会发生数百万次递归,这可能会很慢。您可以在其中添加调试echo 命令以查看进度。如果您想向我提供文件(给我发送链接,或从我的网站向我发送电子邮件 - 在我的个人资料中列出 - 与数据一起),我很乐意用您的 6,000 个元素对其进行测试。关于$root_parent,任何限制递归都将有助于提高性能。您可以将一组较小的元素放在一起,以确保它能够满足您的需求
猜你喜欢
  • 2014-10-13
  • 1970-01-01
  • 2015-05-17
  • 2023-03-18
  • 2015-10-23
  • 2022-11-24
  • 2021-07-15
相关资源
最近更新 更多