在php中建立树形结构数组的所有路径的路径 [英] Build paths of all paths of a tree structured array in php

查看:88
本文介绍了在php中建立树形结构数组的所有路径的路径的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试打印类别及其子级,就像db中的树路径一样.到现在为止,我可以像下面那样打印树状结构的数组.

Hi I am trying to print categories and their children like a tree paths from db. Till now i am able to print the tree structured array like below.

Array
(
[0] => Array
    (
        [name] => 'Furniture'
        [slug] => 'furniture'
        [children] => Array
            (
                [0] => Array
                    (
                        [name] => 'Sofa'
                        [slug] => 'sofa'
                        [leafs] => Array
                            (
                               [0] => Array ( [name] => '3 Seater', [slug] = '3-seater'
                               [1] => Array ( [name] => '4 Seater', [slug] = '4-seater'
                            )

                    )

                [1] => Array
                    (
                        [name] => 'Chairs'
                        [slug] => 'chairs'
                        [leafs] => Array
                            (
                               [0] => Array ( [name] => '3 Seater', [slug] = '3-seater'
                               [1] => Array ( [name] => '4 Seater', [slug] = '4-seater'
                            )

                    )

            )

    )

[1] => Array
    (
        [name] => 'Furniture1'
        [slug] => 'furniture1'
        [children] => Array
            (
                [0] => Array
                    (
                        [name] => 'Sofa1'
                        [slug] => 'sofa1'
                        [leafs] => Array
                            (
                               [0] => Array ( [name] => '3 Seater1', [slug] = '3-seater1'
                               [1] => Array ( [name] => '4 Seater1', [slug] = '4-seater1'
                            )

                    )

                [1] => Array
                    (
                        [name] => 'Chairs1'
                        [slug] => 'chairs1'
                        [leafs] => Array
                            (
                               [0] => Array ( [name] => '3 Seater1', [slug] = '3-seater1'
                               [1] => Array ( [name] => '4 Seater1', [slug] = '4-seater1'
                            )

                    )

            )

    )

)

有些孩子可能有叶子,有些父母可能有孩子.但是我要打印的内容如下所示.

Some of the children may have leafs or some of the parents may have children. But what i am trying to print was like below.

Array(
[0] => 'Furniture/Sofa/3 Seater',
[1] => 'Furniture/Sofa/4 Seater',
[2] => 'Furniture/Chiars/ 3 Seater'
[3] => 'Furniture/Chiars/4 Seater',
[4] => 'Furniture1/Sofa1/3 Seater1',
[5] => 'Furniture1/Sofa/4 Seater1',
[6] => 'Furniture1/Chiars1/ 3 Seater1'
[7] => 'Furniture1/Chiars1/4 Seater1',
);

推荐答案

它是多路树".

更新:这是作为类TreePaths

Pastebin.com上的原始完整源代码-此答案描述了新代码.

更改:

  • 输入array node现在仅需要children条目,而不是'leafs'.这使输入更加一致.

  • The input array node now only needs the children entry rather than 'leafs`. This makes the input more consistent.

此代码检查的array node中唯一的条目是$node['children].这样一来,您便可以在节点中拥有任何其他数据,并且可以以任何所需的方式在callable中对其进行处理.

The only entry in the array node checked by this code is the $node['children]. This allows you have any other data in the node that you wish and you can process it in the callable in any way that you wish.

可以用任何具有以下特征的callable代替节点处理:

The node processing can be replaced by any callable having the signature of:

function(array $currentPath, /* boolean */ $isTopALeaf) {...

如果nodeProcessor(可调用)返回的值不为空,则它将添加到$allPaths数组中.

If the nodeProcessor (callable) returns a value that is not empty then it will be added to the $allPaths array.

  • The TreePaths class source code
  • The TreePaths Test Data

网站:

  • Website showing the 'default nodeProcessor' - run1.php
  • Website showing the 'HTML Anchor nodeProcessor' - run2.php

有趣的是,我们必须存储叶"节点的路径". 路径"似乎是特殊的".但是,想象一下,每次递归"或嵌套"到一个级别时,您都会记录自己在堆栈"中的位置. 当您到达当前列表的末尾时,您将:

The interesting part is that we have to store the 'path' to the 'leaf' nodes. The 'path' seems to be 'special'. However, imagine every time you 'recurse' or 'nest' down one level then you record where you are on a 'stack'. When you reach the end of the ' the current list you:

  • 记录通过堆栈"到当前叶节点"的路径
  • 致电nodeProcessor
  • 'unnest'或'pop'栈顶.

我使用提供的树结构"和名称.

I use the 'tree structure' and names as provided.

在每个路径的末尾,用the current path

At the end of every path, the nodeProcessor is called with the current path

输出:

  • 通往任何特定叶子"节点的完整路径".
  • 每个叶子"节点的所有路径.

由于它是一棵树,因此需要递归"算法,因为树的深度"是未知的.

As it is a tree then a 'recursive' algorithm is required as the 'depth' of the tree is unknown.

该程序必须:

  • 遍历树".
  • 保留节点名称的堆栈"作为当前节点的路径"
  • 根据需要在所选节点上运行用户代码.我在这段代码中为此使用了关闭".
  • 将输出存储在有用的地方.
/*
 * Generate an HTML anchor tag from the 'slug' entries
 */
$slugNodeProcessor =
    function(array $currentPath,
                     $isTopALeaf)  {

        /*
         * Lets make some HTML anchors for the Slugs?
         */
        $template = '<a href="%s" class="Slug">%s</a>';

        // top of the stack will be a leaf
        $title = '';
        $top  = end($currentPath); // easier to work with
        $prev = prev($currentPath); // need the description for title
        $title = $prev['name'] .' - '.  $top['name'];

        $url = '/';
        foreach($currentPath as $key => $node) {
                $url .= $node['Slug'] .'/';
        };
        $url = rtrim($url, '/');
        $htmlSlug = sprintf($template, $url, $title);

        return $htmlSlug;
     };

默认的"nodeProcessor":

/**
     * If you don't provide a callable to generate paths then this will be used.
     *
     * It generates a string of names separated by '/'. i.e. it looks like a filepath.
     *
     * @return string
     */
    public function defaultNodeProcessor()
    {
        $dnp = function(array $currentPath,
                         $isTopALeaf)  {

                $fullPath = '/';
                foreach($currentPath as $key => $node) {
                    $fullPath .= $node['name'] .'/';
                }
                return rtrim($fullPath, '/');
             };
         return $dnp;
    }

运行程序:

$tree = new TreePaths($srcTree,
                      $slugNodeProcessor);
$tree->generate();
$allPaths = $tree->allPaths();

输出:

array (8) [
    string (67) "<a href="/furniture/sofa/3-seater" class="Slug">Sofa - 3 Seater</a>"
    string (67) "<a href="/furniture/sofa/4-seater" class="Slug">Sofa - 4 Seater</a>"
    string (76) "<a href="/furniture/sofa/chairs/3-seater" class="Slug">Chairs - 3 Seater</a>"
    string (76) "<a href="/furniture/sofa/chairs/4-seater" class="Slug">Chairs - 4 Seater</a>"
    string (94) "<a href="/furniture/sofa/chairs/furniture1/sofa1/3-seater1" class="Slug">Sofa1 - 3 Seater1</a>"
    string (94) "<a href="/furniture/sofa/chairs/furniture1/sofa1/4-seater1" class="Slug">Sofa1 - 4 Seater1</a>"
    string (104) "<a href="/furniture/sofa/chairs/furniture1/sofa1/chairs1/3-seater1" class="Slug">Chairs1 - 3 Seater1</a>"
    string (104) "<a href="/furniture/sofa/chairs/furniture1/sofa1/chairs1/4-seater1" class="Slug">Chairs1 - 4 Seater1</a>"
]

课程:

/*
 * Source Tree:
 *   
 * Tree Node:
 *
 * Array(
 *        "name" => 'Furniture',  // not checked
 *        "slug" => 'furniture',  // optional - not used
 *        "children" => Array( // will be other Tree nodes...
 *                          ),
 *      );
 *
 *  The `children` key is optional, if empty or missing, means it is a `leaf` node
 *
 *  !!! Note: The only array entry checked in here is 'children' !!!
 *
 *  But you will need to overide the default nodeProcessor.
 *
 *  The default `nodeProcessor` uses `name` and `children` only
 */

/*
 * NodeProcessor:
 *   o It is a callable that accepts two parameters
 *     o current path - an array of all the nodes so far in this path
 *     o isTopALeaf   - is the end of the path a 'leaf' node?
 */

/**
 *  Traverse the tree of `nodes`
 *  Generate a list of Paths from Root to every leaf node as an array of `nodes`.
 *  It is a `stack` with the top node being a leaf.
 */
class TreePaths {

    /**
     * The 'current' menu / tree
     *
     * @var array $tree
     */
    private $tree = array();


    /**
     * The Output
     *
     * @var array $allPaths
     */
    private $allPaths = array();

    /**
     * The 'current' stack of nodes in this path
     *
     * This is a 'stack'. The 'path' is all the entries combined.
     *
     * @var array $currentPath
     */
    private $currentPath = array();


    /**
     * The 'callable' to be run for nodes
     *
     * @var callable $nodeProcessor
     */
    private $nodeProcessor = null;


    /**
     * Call All Nodes or Leaf node only
     *
     * @var boolean
     */
    private $callLeafNodesOnly = true;


    /**
     * Build the class but do not run it...
     *
     * Provides a default NodeProcessor if you don't provide one.
     *    o The default processor builds string paths that look like filepaths
     *
     * @param array $tree
     * @param callable $processNode        - optional
     * @param boolean $callLeafNodesOnly  - optional default true
     */
    public function __construct(array $tree,
                                   /* callable */ $processNode = null,
                                   $callLeafNodesOnly = true)
    {
        $this->tree = $tree;
        $this->nodeProcessor = $processNode;
        $this->callLeafNodesOnly = $callLeafNodesOnly;

        // provide a default processor
        if (is_null($this->nodeProcessor)) {
            $this->nodeProcessor = $this->defaultNodeProcessor();
        }
    }

    /**
     * This routine makes this class rather powerful as you can use any callable.
     *
     * @param type $nodeProcessor
     */
    public function setNodeProcessor(/* callable */ $nodeProcessor)
    {
        $this->nodeProcessor = $nodeProcessor;
    }

    /**
     * Return a list of all the paths that were generated by the 'nodeProcessor'
     * @return array
     */
    public function allPaths()
    {
        return $this->allPaths;
    }

    /**
     * The routine that processes one node and recurses as required
     *
     * @param array $node
     * @return void This is all side-effects
     */
    protected function treeWalk($node)
    {
        // always add the node to the currentPath
        array_push($this->currentPath, $node);

        // Always call the node processor and add the path to all paths if required
        $processedPath = $this->callNodeProcessor($this->currentPath,
                                                  $this->isLeafNode($node));
        if (!empty($processedPath)) { // add to all the paths
            $this->allPaths[] = $processedPath;
        }

        // do we recurse?
        if ($this->isLeafNode($node)) { // no we dont...
            array_pop($this->currentPath); // lose leaf node from top of stack
            return; // nothing more to do
        }

        // now process all the children... This will recurse - always
        foreach ($node['children'] as $key => $node) {
            $this->treeWalk($node);
        }
        return; // end of children
    }

    /**
     * Process all the top level nodes.
     *
     * @return void
     */
    public function generate()
    {
        $this->allPaths = array();

        foreach ($this->tree as $key => $node) {
            $this->treeWalk($node);
        }
        return;
    }

    /**
     * End of a path?
     *
     * @param array $node
     * @return boolean
     */
    protected function isLeafNode($node)
    {
        return empty($node['children']);
    }

    /**
     * Are we in the 'middle' of a path?
     *
     * @param array $node
     * @return boolean
     */
    protected function hasChildren($node)
    {
        return !empty($node['children']);
    }


    /**
     * The `callable` to be called.
     *
     * It must accept the two parameters.
     *
     * It can be set after the 'class instance' is created.
     *
     * @param array currentPath to this value
     * @param string nodeType - leaf or children
     *
     * @return mixed if not empty will be added to the paths
     */
    protected function callNodeProcessor(array $currentPath,
                                             $isTopALeaf)
    {

        if ($this->callLeafNodesOnly) {
            if ($isTopALeaf)  {
                return call_user_func($this->nodeProcessor,
                                       $currentPath,
                                       $isTopALeaf);
            }
        }
        else {
            return call_user_func($this->nodeProcessor,
                                   $currentPath,
                                   $isTopALeaf);
        }
    }

    /**
     * If you don't provide a callable to generate paths then this will be used.
     *
     * It generates a string of names separated by '/'. i.e. it looks like a filepath.
     *
     * @return string
     */
    public function defaultNodeProcessor()
    {
        $dnp = function(array $currentPath,
                         $isTopALeaf)  {

                $fullPath = '/';
                foreach($currentPath as $key => $node) {
                    $fullPath .= $node['name'] .'/';
                }
                return rtrim($fullPath, '/');
             };
         return $dnp;
    }
}

源数据格式:

/*
 * Tree Node:
 *
 * Array(
 *        "name" => 'Furniture',
 *        "slug" => 'furniture',
 *        "children" => Array( // can be other Tree nodes...
 *                          ),
 *      );
 *
 *  The `children` key is optional, if empty or missing, means it is a `leaf` node
 *
 *  !!! Note: The only array entry checked in here is 'children' !!!
 *
 *  But you would need to overide the default nodeProcessor.
 */

样本测试数据

$srcTree = Array(
  0 => Array(
          "name" => 'Furniture',
          "Slug" => 'furniture',
          "children" => Array(
                  "0" => Array
                      (
                          "name" => 'Sofa',
                          "Slug" => 'sofa',
                          "children" => Array
                              (
                                 "0" => Array ( "name" => '3 Seater', "Slug" => '3-seater'),
                                 "1" => Array ( "name" => '4 Seater', "Slug" => '4-seater'),
                              ),

                      ),

                  "1" => Array
                      (
                          "name" => 'Chairs',
                          "Slug" => 'chairs',
                          "children" => Array
                              (
                                 "0" => Array ( "name" => '3 Seater', "Slug" => '3-seater'),
                                 "1" => Array ( "name" => '4 Seater', "Slug" => '4-seater'),
                              )

                      )

              )

      ),
      More entries here ...

这篇关于在php中建立树形结构数组的所有路径的路径的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆