将键值对的PHP数组转换为分层嵌套树结构

时间:2014-05-21 04:17:59

标签: php arrays algorithm recursion iterator

我的情况是我有一个由大约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)我尝试将数据保存在数据库中,因为我非常熟悉使用左/右值来访问/操作数据作为嵌套集,但我试图避免必须从中获取所有INSERT / SELECT数据库。

4)我尝试使用各种PHP Iterators类,因为我已成功使用它们来处理文件系统和构建文件/目录列表,所以我一直在玩RecursiveArrayIterator / ParentIterator / ArrayIterator,但不能似乎要弄清楚要使用的正确语法。

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

除此之外,我还试图更好地理解程序化递归的算法性质。

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

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

澄清说明

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

1 个答案:

答案 0 :(得分: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
                        (
                        )

                )

        )

)

enter image description here

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

输出(对于更新#1):

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

                )

            [child2] => 
        )

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

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

)

enter image description here

代码(更新#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>";
        }
    }

?>