Convert a series of parent-child relationships into a hierarchical tree?

前端 未结 11 1915
[愿得一人]
[愿得一人] 2020-11-22 07:04

I have a bunch of name-parentname pairs, that I\'d like to turn into as few heirarchical tree structures as possible. So for example, these could be the pairings:

         


        
相关标签:
11条回答
  • 2020-11-22 07:30

    Well, first I would turn the straight array of key-value pairs into a hierarchical array

    function convertToHeiarchical(array $input) {
        $parents = array();
        $root = array();
        $children = array();
        foreach ($input as $item) {
            $parents[$item['id']] = &$item;
            if ($item['parent_id']) {
                if (!isset($children[$item['parent_id']])) {
                    $children[$item['parent_id']] = array();
                }
                $children[$item['parent_id']][] = &$item;
            } else {
                $root = $item['id'];
            }
        }
        foreach ($parents as $id => &$item) {
            if (isset($children[$id])) {
                $item['children'] = $children[$id];
            } else {
                $item['children'] = array();
            }
        }
        return $parents[$root];
    }
    

    That'll can convert a flat array with parent_id and id into a hierarchical one:

    $item = array(
        'id' => 'A',
        'blah' => 'blah',
        'children' => array(
            array(
                'id' => 'B',
                'blah' => 'blah',
                'children' => array(
                    array(
                        'id' => 'C',
                        'blah' => 'blah',
                        'children' => array(),
                    ),
                 ),
                'id' => 'D',
                'blah' => 'blah',
                'children' => array(
                    array(
                        'id' => 'E',
                        'blah' => 'blah',
                        'children' => array(),
                    ),
                ),
            ),
        ),
    );
    

    Then, just create a rendering function:

    function renderItem($item) {
        $out = "Your OUtput For Each Item Here";
        $out .= "<ul>";
        foreach ($item['children'] as $child) {
            $out .= "<li>".renderItem($child)."</li>";
        }
        $out .= "</ul>";
        return $out;
    }
    
    0 讨论(0)
  • 2020-11-22 07:31

    Yet Another Function To Make A Tree (no recursion involved, uses references instead):

    $array = array('H' => 'G', 'F' => 'G', ..., 'D' => null);
    
    function to_tree($array)
    {
        $flat = array();
        $tree = array();
    
        foreach ($array as $child => $parent) {
            if (!isset($flat[$child])) {
                $flat[$child] = array();
            }
            if (!empty($parent)) {
                $flat[$parent][$child] =& $flat[$child];
            } else {
                $tree[$child] =& $flat[$child];
            }
        }
    
        return $tree;
    }
    

    Returns an hierarchical array like this one:

    Array(
        [D] => Array(
            [G] => Array(
                [H] => Array()
                [F] => Array()
            )
            ...
        )
    )
    

    Which can easily be printed as a HTML list using recursive function.

    0 讨论(0)
  • 2020-11-22 07:32
    $tree = array(
        'H' => 'G',
        'F' => 'G',
        'G' => 'D',
        'E' => 'D',
        'A' => 'E',
        'B' => 'C',
        'C' => 'E',
        'D' => null,
        'Z' => null,
        'MM' =>'Z',
        'KK' =>'Z',
        'MMM' =>'MM',
        // 'MM'=>'DDD'
    );
    

    $aa=$this->parseTree($tree);

    public function get_tress($tree,$key)
    {
    
        $x=array();
        foreach ($tree as $keys => $value) {
            if($value==$key){
            $x[]=($keys);
            }
        }
        echo "<li>";
        foreach ($x as $ke => $val) {
        echo "<ul>";
            echo($val);
            $this->get_tress($tree,$val);
        echo "</ul>";
        }
        echo "</li>";
    
    
    }
    function parseTree($tree, $root = null) {
    
        foreach ($tree as $key => $value) {
            if($value==$root){
    
                echo "<ul>";
                echo($key);
                $this->get_tress($tree,$key);
                echo "</ul>";
            }
        }
    
    0 讨论(0)
  • 2020-11-22 07:34

    Parent-child relationship nested Array
    Fetch all the record from the database and creating nested array.

    $data = SampleTable::find()->all();
    $tree = buildTree($data);
    print_r($tree);
    
    public function buildTree(array $elements, $parentId = 0) {
        $branch = array();
        foreach ($elements as $element) {
            if ($element['iParentId'] == $parentId) {
                $children =buildTree($elements, $element['iCategoriesId']);
                if ($children) {
                    $element['children'] = $children;
                }
                $branch[] = $element;
            }
        }
        return $branch;
    }
    

    Print Categories and Sub-categories data in json Format

    public static function buildTree(array $elements, $parentId = 0){
        $branch = array();
        foreach($elements as $element){
            if($element['iParentId']==$parentId){
                $children =buildTree($elements, $element['iCategoriesId']);
                if ($children) {
                    $element['children'] = $children;
    
                }
                    $branch[] = array(
                        'iCategoriesId' => $element->iCategoriesId,
                        'iParentId'=>$element->iParentId,
                        'vCategoriesName'=>$element->vCategoriesName,
                        'children'=>$element->children,
                );
            }
        }
        return[
            $branch
        ];
    }
    
    0 讨论(0)
  • 2020-11-22 07:34

    Old question, but I too had to do this and the examples with recursion gave me a headache. In my database we have a locations table, which was a loca_id PK (Child) and self referencing loca_parent_id (Parent).

    The aim is to represent this structure in HTML. The simple query can of couyrse return the data is some fixed order but I found not well enough to display such data in a natural way. What I really wanted was Oracle tree walk handling with LEVEL to help with display.

    I decided to use the idea of a 'path' to uniquely identify each entry. For example:

    Sorting the array by the path should make it easier to process for meaningful display.

    I realise that use of associative arrays and sorts is cheating as it hides the recursive complexity of the operations, but to me this look simpler:

    <table>
    <?php
        
        $sql = "
        
        SELECT l.*,
               pl.loca_name parent_loca_name,
               '' loca_path
        FROM locations l
        LEFT JOIN locations pl ON l.loca_parent_id = pl.loca_id
        ORDER BY l.loca_parent_id, l.loca_id
        
        ";
        
        function print_row ( $rowdata )
        {
        ?>
                          <tr>
                              <td>
                                  <?=$rowdata['loca_id']?>
                              </td>
                              <td>
                                  <?=$rowdata['loca_path']?>
                              </td>
                              <td>
                                  <?=$rowdata['loca_type']?>
                              </td>
                              <td>
                                  <?=$rowdata['loca_status']?>
                              </td>
                          </tr>
        <?php
        
        }
        
        $stmt  = $dbh->prepare($sql);
        $stmt->execute();
        $result = $stmt->get_result();
        $data = $result->fetch_all(MYSQLI_ASSOC);
        
        $printed = array();
        
        // To get tree hierarchy usually means recursion of data.
        // Here we will try to use an associate array and set a
        // 'path' value to represent the hierarchy tree in one
        // pass. Sorting this array by the path value should give
        // a nice tree order and reference.
    // The array key will be the unique id (loca_id) for each row.
    // The value for each key will the complete row from the database.
    // The row contains a element 'loca_path' - we will write the path
    // for each row here. A child's path will be parent_path/child_name.
    // For any child we encounter with a parent we look up the parents path
    // using the loca_parent_id as the key.
    // Caveat, although tested quickly, just make sure that all parents are
    // returned first by the query.
        
        foreach ($data as $row)
        {
        
           if ( $row['loca_parent_id'] == '' ) // Root Parent
           {
              $row['loca_path'] = $row['loca_name'] . '/';
              $printed[$row['loca_id']] = $row;
           }
           else // Child/Sub-Parent
           {
              $row['loca_path'] = $printed[$row['loca_parent_id']]['loca_path'] . $row['loca_name'] . '/';
              $printed[$row['loca_id']] = $row;
           }
        }
        
        // Array with paths built, now sort then print
        
        array_multisort(array_column($printed, 'loca_path'), SORT_ASC, $printed);
        
        foreach ( $printed as $prow )
        {
           print_row ( $prow );
        }
        ?>
        </table>
    
    0 讨论(0)
  • 2020-11-22 07:35

    This requires a very basic recursive function to parse the child/parent pairs to a tree structure and another recursive function to print it out. Only one function would suffice but here's two for clarity (a combined function can be found at the end of this answer).

    First initialize the array of child/parent pairs:

    $tree = array(
        'H' => 'G',
        'F' => 'G',
        'G' => 'D',
        'E' => 'D',
        'A' => 'E',
        'B' => 'C',
        'C' => 'E',
        'D' => null
    );
    

    Then the function that parses that array into a hierarchical tree structure:

    function parseTree($tree, $root = null) {
        $return = array();
        # Traverse the tree and search for direct children of the root
        foreach($tree as $child => $parent) {
            # A direct child is found
            if($parent == $root) {
                # Remove item from tree (we don't need to traverse this again)
                unset($tree[$child]);
                # Append the child into result array and parse its children
                $return[] = array(
                    'name' => $child,
                    'children' => parseTree($tree, $child)
                );
            }
        }
        return empty($return) ? null : $return;    
    }
    

    And a function that traverses that tree to print out an unordered list:

    function printTree($tree) {
        if(!is_null($tree) && count($tree) > 0) {
            echo '<ul>';
            foreach($tree as $node) {
                echo '<li>'.$node['name'];
                printTree($node['children']);
                echo '</li>';
            }
            echo '</ul>';
        }
    }
    

    And the actual usage:

    $result = parseTree($tree);
    printTree($result);
    

    Here's the contents of $result:

    Array(
        [0] => Array(
            [name] => D
            [children] => Array(
                [0] => Array(
                    [name] => G
                    [children] => Array(
                        [0] => Array(
                            [name] => H
                            [children] => NULL
                        )
                        [1] => Array(
                            [name] => F
                            [children] => NULL
                        )
                    )
                )
                [1] => Array(
                    [name] => E
                    [children] => Array(
                        [0] => Array(
                            [name] => A
                            [children] => NULL
                        )
                        [1] => Array(
                            [name] => C
                            [children] => Array(
                                [0] => Array(
                                    [name] => B
                                    [children] => NULL
                                )
                            )
                        )
                    )
                )
            )
        )
    )
    

    If you want a bit more efficiency, you can combine those functions into one and reduce the number of iterations made:

    function parseAndPrintTree($root, $tree) {
        $return = array();
        if(!is_null($tree) && count($tree) > 0) {
            echo '<ul>';
            foreach($tree as $child => $parent) {
                if($parent == $root) {                    
                    unset($tree[$child]);
                    echo '<li>'.$child;
                    parseAndPrintTree($child, $tree);
                    echo '</li>';
                }
            }
            echo '</ul>';
        }
    }
    

    You'll only save 8 iterations on a dataset as small as this but on bigger sets it could make a difference.

    0 讨论(0)
提交回复
热议问题