Переупорядочить многомерный массив в иерархию, используя значение дочернего элемента - PullRequest
0 голосов
/ 29 апреля 2020

Мне нужно преобразовать плоский многомерный массив в иерархию, используя group_parent_id в отдельных массивах в качестве ссылки.

Одно неудобство заключается в том, что я могу работать только с подмножеством групп, которые пользователь имеет доступ к нему, поэтому, хотя Test group сам является дочерним, его родительского элемента фактически нет в списке из-за прав доступа. Так что Test group будет считаться root уровнем с точки зрения этого пользователя

К сожалению, это попадает в рекурсию, которая не является моей сильной стороной ...

Ввод:

$arr = [
    [
        'group_id' => 5,
        'group_title' => 'Test group',
        'group_parent_id' => 2
    ],
    [
        'group_id' => 6,
        'group_title' => 'Second-level test group',
        'group_parent_id' => 5
    ],
    [
        'group_id' => 7,
        'group_title' => 'Third-level test group',
        'group_parent_id' => 6
    ],
    [
        'group_id' => 8,
        'group_title' => 'Other test group',
        'group_parent_id' => 1
    ],
    [
        'group_id' => 9,
        'group_title' => 'Second-level other test group',
        'group_parent_id' => 8
    ]
];

Желаемый результат:

$arr = [
    [
        'group_id' => 5,
        'group_title' => 'Test group',
        'group_parent_id' => 2,
        'subgroups' => [
            [
                'group_id' => 6,
                'group_title' => 'Second-level test group',
                'group_parent_id' => 5,
                'subgroups' => [
                    [
                        [
                            'group_id' => 7,
                            'group_title' => 'Third-level test group',
                            'group_parent_id' => 6
                        ]
                    ]
                ]
            ]
        ]
    ],
    [
        'group_id' => 8,
        'group_title' => 'Other test group',
        'group_parent_id' => 1,
        'subgroups' => [
            [
                'group_id' => 9,
                'group_title' => 'Second-level other test group',
                'group_parent_id' => 8
            ]
        ]
    ]
];

Я пробовал несколько разных вещей, но мне не удалось достичь желаемого результата, требуемого выше:

function build_group_tree(array $groups, $group_ids = [])
{
    $output = [];

    foreach ($groups as $group) {
        if (in_array($group['group_parent_id'], $group_ids)) {
            $children = build_group_tree($groups, $group['group_id']);

            if ($children) {
                $group['subgroups'] = $children;
            }

            $output[] = $group;
        }
    }

    return $output;
}

build_group_tree($arr, array_column($arr, 'group_id));

Это дает мне частичный результат, но не включает все группы.

Любая помощь приветствуется.

1 Ответ

1 голос
/ 29 апреля 2020

Один из способов сделать это - выполнить итерацию по массиву, создать список ссылок на каждую запись group_id в выходном массиве, а затем добавить потомков по ссылке:

$output = array();
$group_ids = array();
foreach ($arr as $el) {
    $parent_id = $el['group_parent_id'];
    if (!isset($group_ids[$parent_id])) {
        // must be a top-level element
        $output[] = $el;
        // save a pointer
        $group_ids[$el['group_id']] = &$output[array_key_last($output)];
    }
    else {
        // already exists, $group_ids[$parent_id] points to the entry
        $parent = &$group_ids[$parent_id];
        if (!isset($parent['subgroups'])) {
            // no subgroups array, create one
            $parent['subgroups'] = array();
        }
        // add this entry to the subgroups
        $parent['subgroups'][] = $el;
        // save a pointer
        $group_ids[$el['group_id']] = &$parent['subgroups'][array_key_last($parent['subgroups'])];
    }
}
print_r($output);

Вывод:

Array
(
    [0] => Array
        (
            [group_id] => 5
            [group_title] => Test group
            [group_parent_id] => 2
            [subgroups] => Array
                (
                    [0] => Array
                        (
                            [group_id] => 6
                            [group_title] => Second-level test group
                            [group_parent_id] => 5
                            [subgroups] => Array
                                (
                                    [0] => Array
                                        (
                                            [group_id] => 7
                                            [group_title] => Third-level test group
                                            [group_parent_id] => 6
                                        )
                                )
                        )
                )
        )
    [1] => Array
        (
            [group_id] => 8
            [group_title] => Other test group
            [group_parent_id] => 1
            [subgroups] => Array
                (
                    [0] => Array
                        (
                            [group_id] => 9
                            [group_title] => Second-level other test group
                            [group_parent_id] => 8
                        )
                )
        )
)

Демонстрация на 3v4l.org

Обратите внимание, что этот код использует array_key_last, который был введен только в PHP в версия 7.3. Если у вас более ранняя версия, вместо нее используйте count($array)-1.

...