How do I format Nested Set Model data into an array?

前端 未结 5 1348
长发绾君心
长发绾君心 2020-12-09 14:18

Let\'s dig in the main problem right away, I have the input like this

$category = array(
  \'A\' => array(\'left\' => 1, \'right\' => 8),
  \'B\' =&         


        
相关标签:
5条回答
  • 2020-12-09 14:29

    Working with a nested set is a perfect case for recursion.

    Given your data:

    $category = array(
        'A' => array('left' => 1, 'right' => 9),
        'B' => array('left' => 2, 'right' => 4),
        'C' => array('left' => 5, 'right' => 8),
        'D' => array('left' => 6, 'right' => 7),
        'E' => array('left' => 10, 'right' => 11),
    );
    

    The following will break your nested set data down into a properly nested array in PHP:

    function createTree($category, $left = 0, $right = null) {
        $tree = array();
        foreach ($category as $cat => $range) {
            if ($range['left'] == $left + 1 && (is_null($right) || $range['right'] < $right)) {
                $tree[$cat] = createTree($category, $range['left'], $range['right']);
                $left = $range['right'];
            }
        }
        return $tree;
    }
    
    $tree = createTree($category);
    print_r($tree);
    

    Output:

    Array
    (
        [A] => Array
            (
                [B] => Array
                    (
                    )
    
                [C] => Array
                    (
                        [D] => Array
                            (
                            )
    
                    )
    
            )
    
        [E] => Array
            (
            )
    
    )
    

    Then you can flatten your proper tree into the format you want with the following:

    function flattenTree($tree, $parent_tree = array()) {
        $out = array();
        foreach ($tree as $key => $children) {
            $new_tree = $parent_tree;
            $new_tree[] = $key;
            if (count($children)) {
                 $child_trees = flattenTree($children, $new_tree);
                foreach ($child_trees as $tree) {
                    $out[] = $tree;
                }
            } else {
                $out[] = $new_tree;
            }
        }
        return $out;
    }
    
    $tree = flattenTree($tree);
    print_r($tree);
    

    Output:

    Array
    (
        [0] => Array
            (
                [0] => A
                [1] => B
            )
    
        [1] => Array
            (
                [0] => A
                [1] => C
                [2] => D
            )
    
        [2] => Array
            (
                [0] => E
            )
    
    )
    
    0 讨论(0)
  • 2020-12-09 14:35

    I little modified Stiven's code.

    public function createTree($category, $left = 0, $right = null) {
        $tree = array();
        foreach ($category as $cat => $range) {
            if ($range['clf'] == $left + 1 && (is_null($right) || $range['crt'] < $right)) {
                $tree[$cat]= array();
                $tree[$cat]['title']=$range['title'];
                if($range['crt']-$range['clf']>1){
                    $tree[$cat]['sub'] = $this->createTree($category, $range['clf'], $range['crt']);
                }
                $left = $range['crt'];
            }
        }
        return $tree;
    }
    
    0 讨论(0)
  • 2020-12-09 14:37

    If you dont want to use recursion:

    foreach ($category as $name => $range) {
        $line[$range['left']] = $name;
        $line[$range['right']] = $name;
    }
    
    ksort($line);
    $count = 0;
    
    foreach($line as $name) {
        if ( ! isset($open[$name])) {
            $open[$name] = true;
            $result[$name] = true;
            $count++;
        } else {
            unset($open[$name]);
            if ($count > 0) {
                $count = 0;
                $tree[] = array_keys($result);
                $result = $open;
            } else {
                $result = array();
            }
        }
    }
    
    0 讨论(0)
  • 2020-12-09 14:43

    Another solution, without recursion (test please)

    $result = array();
    
        foreach($category as $key => $value) {
    
            /*Get current row index*/
            $i = count($result);
    
            if($i == 0) {
                $result[] = array($key);
            } else {
    
                $iParent = -1;
    
                /*Find parent index*/
                for($j = count($result[$i-1]) - 1; $j >= 0; $j--) {
                    if($value['left'] > $category[$result[$i-1][$j]]['left'] 
                        && $value['right'] < $category[$result[$i-1][$j]]['right']) {
                        $iParent = $j;
                        break;
                    }
                }
    
                if($iParent == -1) { $result[] = array($key);}
    
                if($iParent == count($result[$i-1]) - 1) {
                    // append to last
                    $result[$i-1][] = $key;
                } else {
                    // make new list
                    $result[$i] = array_slice($result[$i-1], 0, $iParent + 1);
                    $result[$i][] = $key;
                }
            }
        }
    
        print_r($result);
    
    0 讨论(0)
  • 2020-12-09 14:46

    There is a bug with above function. The top category of second array of the @tree is removed. This is the fix:

    foreach ($category as $name => $range) {
        $line[$range['left']] = $name;
        $line[$range['right']] = $name;
    }
    
    ksort($line);
    $tree = array();
    $count = 0;
    
    foreach ($line as $name) {
        if (!isset($open[$name])) {
            $open[$name] = true;
            $count++;
        }
        else {
            if ($count > 0) {
                $count = 0;
                $tree[] = array_keys($open);
            }
            unset($open[$name]);
        }
    }
    
    0 讨论(0)
提交回复
热议问题