Построить дерево Nested Set

197
15 января 2019, 21:30

Из плоского массива:

$tmp = array(
    array('id' => 0, 'level' => 0, 'name' => 'root', 'left' => 1, 'right' => 13),
    array('id' => 1, 'level' => 1, 'name' => 'Техотдел', 'left' => 2, 'right' => 8),
    array('id' => 2, 'level' => 1, 'name' => 'Отдел кадров', 'left' => 9, 'right' => 10),
    array('id' => 3, 'level' => 1, 'name' => 'Экономический отдел', 'left' => 11, 'right' => 12),
    array('id' => 4, 'level' => 2, 'name' => 'Подотдел техотдела 1', 'left' => 3, 'right' => 6),
    array('id' => 5, 'level' => 2, 'name' => 'Подотдел техотдела 2', 'left' => 7, 'right' => 8),
    array('id' => 6, 'level' => 3, 'name' => 'Подподотдел техотдела 1', 'left' => 4, 'right' => 5),
);

Необходимо построить дерево вида:

$tree = array(
array(
    'id' => 0,
    'level' => 0,
    'name' => 'root',
    'children' => array(
        array(
            'id' => 1,
            'level' => 1,
            'name' => 'Техотдел',
            'children' => array(
                array(
                    'id' => 4,
                    'level' => 2,
                    'name' => 'Подотдел техотдела 1',
                    'children' => array(
                        array(
                            'id' => 6,
                            'level' => 3,
                            'name' => 'Подподотдел техотдела 1'
                        ),
                    ),
                ),
                array(
                    'id' => 5,
                    'level' => 2,
                    'name' => 'Подотдел техотдела 2'
                ),
            ),
        ),
        array(
            'id' => 2,
            'level' => 1,
            'name' => 'Отдел кадров'
        ),
        array(
            'id' => 3,
            'level' => 1,
            'name' => 'Экономический отдел'
        ),
    )
),
);

Голову ломаю, не могу сообразить как это сделать.

Answer 1

Наверное, алгоритм решения не один существует, так что простой вариант построения:

Сначала определим максимальную глубину и разложим массив на уровни:

$lmax = max(array_column($tmp, 'level'));
$levels = array_fill(0, $lmax, []);
foreach($tmp as $node){
    $levels[$node['level']][] = $node + ['children' => []];
}

Теперь собственно определим функцию построения. На вход нужен родительских узел. Будем перебирать элементы, которые лежат на уровень ниже. Если нижний элемент подходит под граничные условия, то добавляем его к children. Далее запускаем рекурсию.

$build = function(&$root) use (&$build, $levels){
            $l = $root['level'] + 1;
            if(!isset($levels[$l])) return;
            foreach($levels[$l] as &$n){
                if(($n['id'] >= $root['left']) && ($n['id'] <= $root['right'])){
                    $root['children'][] = &$n;
                    $build($n);
                }
            }
        };

Следует обратить внимание на использование ссылок & как в цикле перебора, так и при добавлении в children

Осталось запустить это дело.

$root = $levels[0][0];
$build($root);
print_r($root);

На выходе искомая структура.

В принципе можно попутно удалять элементы из $levels, которые вносим в children, чтобы в следующие разы (в рекурсиях) их уже не перебирать.

READ ALSO
Почему не находится файл, если он есть?

Почему не находится файл, если он есть?

Я всегда получаю not exists даже если файл есть- Что не так ?

172
Удалить символы из строки в PHP

Удалить символы из строки в PHP

Приходит строка с номером телефона в таком формате

216
Как перезаписать значение ключа $data в Codeigniter 3.1.6

Как перезаписать значение ключа $data в Codeigniter 3.1.6

В контроллере, в функции index() я создаю массив:

208
Цифры 2 и 4 в именах функций

Цифры 2 и 4 в именах функций

Часто встречал у разных разработчиков, в названиях методов использую числа 2 или 4

202