【问题标题】:Creating a multilevel array using parentIds in PHP在 PHP 中使用 parentIds 创建多级数组
【发布时间】:2010-02-16 14:12:11
【问题描述】:

我正在尝试设置一个可以具有多个级别的列表,使用parentId 来定义其父级。第一项的parentId 为NULL。部分条目示例:

id parentId name
1    NULL     item1
2    NULL     item2
3    1        item3
4    2        item4
5    3        item5
6    3        item6

所以,1 和 2 是主要项目; 3 是 1 岁的孩子; 4 是 2 岁的孩子; 5 是 3 的孩子(它本身就是 1 的孩子); 6 也是 3 的孩子(它本身就是 1 的孩子);等等

我无法创建一个正确地将这些项目添加到正确级别的数组。它应该是这样的:


Array
(
    [1] => Array
        (
            [name] => item1
            [parentId] => 
            [children] => Array
                (
                    [3] => Array
                        (
                            [name] => item3
                            [parentId] => 1
                            [children] => Array
                                (
                                    [5] => Array
                                        (
                                            [name] => item5
                                            [parentId] => 3
                                        )

                                    [6] => Array
                                        (
                                            [name] => item6
                                            [parentId] => 3
                                        )

                                )

                        )

                )

        )

    [2] => Array
        (
            [name] => item2
            [parentId] => 
            [children] => Array
                (
                    [4] => Array
                        (
                            [name] => item4
                            [parentId] => 2
                        )

                )

        )

)

但是假设我使用foreach() 遍历所有项目,然后我到达项目 5。它的 parentId 是 3,但那时,我不知道这个父 3 在数组中的位置,以及如何将子级添加到该父级。

有没有什么技巧可以循环遍历这些项目,并以正确的方式将它们全部放置到位?

【问题讨论】:

    标签: php arrays


    【解决方案1】:

    来了

    // your original data as an array
    $data = array(
        array(
            'id' => 1,
            'parentId' => null,
            'name' => 'item1'
        ),
        array(
            'id' => 2,
            'parentId' => null,
            'name' => 'item2'
        ),
        array(
            'id' => 3,
            'parentId' => 1,
            'name' => 'item3'
        ),
        array(
            'id' => 4,
            'parentId' => 2,
            'name' => 'item4'
        ),
        array(
            'id' => 5,
            'parentId' => 3,
            'name' => 'item5'
        ),
        array(
            'id' => 6,
            'parentId' => 3,
            'name' => 'item6'
        ),
    );
    

    递归函数

    function buildTree( $ar, $pid = null ) {
        $op = array();
        foreach( $ar as $item ) {
            if( $item['parentId'] == $pid ) {
                $op[$item['id']] = array(
                    'name' => $item['name'],
                    'parentId' => $item['parentId']
                );
                // using recursion
                $children =  buildTree( $ar, $item['id'] );
                if( $children ) {
                    $op[$item['id']]['children'] = $children;
                }
            }
        }
        return $op;
    }
    
    print_r( buildTree( $data ) );
    
    /*
    Array
    (
        [1] => Array
            (
                [name] => item1
                [parentId] => 
                [children] => Array
                    (
                        [3] => Array
                            (
                                [name] => item3
                                [parentId] => 1
                                [children] => Array
                                    (
                                        [5] => Array
                                            (
                                                [name] => item5
                                                [parentId] => 3
                                            )
    
                                        [6] => Array
                                            (
                                                [name] => item6
                                                [parentId] => 3
                                            )
    
                                    )
    
                            )
    
                    )
    
            )
    
        [2] => Array
            (
                [name] => item2
                [parentId] => 
                [children] => Array
                    (
                        [4] => Array
                            (
                                [name] => item4
                                [parentId] => 2
                            )
    
                    )
    
            )
    
    )
    */
    

    【讨论】:

    • 谢谢!正是我想要的。递归部分是诀窍。
    • 非常好!但是,当您拥有 20.000 个数据集和 10 个深度级别时,这会变得非常缓慢。
    【解决方案2】:

    您应该使用项的 ID 作为数组的键值,以便您可以通过这种方式将项添加到其父项:

    $array[$parentID]['children'][$childID] = array();
    

    【讨论】:

      【解决方案3】:

      首先想到的只是你所拥有的平面版本:

      array (
      [0] => array(
          'name' => 'item1',
          'parent' => null
          ),
      [1] => array(
          'name' => 'item2',
          'parent' => null
          ),
      [3] => array(
          'name' => 'item3',
          'parent' => 0
          ),
      [4] => array(
          'name' => 'item4',
          'parent' => 3
          ),
      [5] => array(
          'name' => 'item5',
          'parent' => 1
          ),
      [6] => array(
          'name' => 'item6',
          'parent' => 1
          ), );
      

      基本上,您只会引用父级。要找到所有孩子,您必须遍历数组。不过,初始设置时间会很快。

      想到的第二个,会涉及更多设置,但以后访问时间会少很多:

      array (
      [0] => array(
          'name' => 'item1',
          'parent' => null,
          'children' = array(3)
          ),
      [1] => array(
          'name' => 'item2',
          'parent' => null
          'children' = array(5, 6)
          ),
      [3] => array(
          'name' => 'item3',
          'parent' => 0
          'children' = array(4)
          ),
      [4] => array(
          'name' => 'item4',
          'parent' => 3
          'children' = array()
          ),
      [5] => array(
          'name' => 'item5',
          'parent' => 1
          'children' = array()
          ),
      [6] => array(
          'name' => 'item6',
          'parent' => 1
          'children' = array()
          ), );
      

      在这个中,您会将所有子索引添加到父索引。这将需要更长的时间,但随后的访问时间会很快。在计算父母时,您只需附加到父母的孩子数组。

      第二种方法唯一真正的缺点是,如果您想添加或删除一个项目,您必须记住返回并更新父级的子级数组。

      【讨论】:

        【解决方案4】:

        您可能不再需要新的解决方案,但这对其他用户可能会派上用场:

        // ! assuming $elements is built like: Array( id1 => Array(/*parameters*/) , ...)
        
        function buildTree($elements) {
        
            $e = Array(0 => Array()); // elements array
            $r = Array(0 =>& Array()); // reference array
        
            while ($elements) {// repeat till $elements is empty
        
                // loop through (remaining) elements
                foreach ($elements as $id => $element) {
        
                    $pid = $element['parentId']; // shortcut
        
                    if (array_key_exists($pid,$r)) { // parent already parsed -> can add this element
        
                        // use parent's reference to elements array to add this element ( $r[$pid] =& $e[path][to][$pid] )
                        $r[$pid] ['elements'][$id] = $element;
        
                        // create reference for this element using parent's reference
                        $r[$id] =& $r[$pid]['elements'][$id];
        
                        // unset current element in input array in order to limit while-loop
                        unset($elements[$id]);
        
                    }
        
                }
            }
        
        return $e; // or whatever you need, the reference array can be useful if you need to fetch an element only knowing its id
        
        }
        

        它的作用:

        • 为结果声明一个数组 ($e) 并添加一个根元素 (0/null)
        • 为引用声明一个数组 ($r) 并已将 $r[0] 引用到 $e[0]
        • 遍历输入数组直到它为空
          • 为每个元素做:
          • 检查父元素是否已经被处理,如果是,你知道这个元素属于哪里并且可以添加它
            (这就是阻止我在前几次尝试中让它工作的原因以及为什么需要 while 循环)
          • 使用父级的reference添加当前元素(注意&=之后!)
          • 使用父级引用创建新引用
          • 从输入数组中取消设置当前元素
            (如果你喜欢无限的while 循环,你可以离开这个;))
        • 当然return你需要什么!

        这种方法的好处是:

        • 您减少了foreach 循环的数量(根据您目前所知道的情况,您可以分配最多)
        • 每个循环都变短了,因为您在输入数组中取消了已解析的元素 ($elements)
        • 您还可以返回引用数组,并能够快速获取元素的参数(假设您知道它的 id)

        换句话说:它应该更快(我还没有测试过!),至少对于像我使用它的更复杂的任务。

        【讨论】:

          猜你喜欢
          • 2013-04-14
          • 1970-01-01
          • 2013-01-29
          • 1970-01-01
          • 2018-06-08
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多