#php #arrays #recursion #hierarchical-data
#php #массивы #рекурсия #иерархический-данные
Вопрос:
Я не уверен, что название правильное, так как это трудно объяснить. Я хочу преобразовать 1-мерный массив в многомерный массив на основе количества хэштегов каждого массива.
Итак, в основном я хочу это.
[
0 => "# Grandparent #1"
1 => "# Grandparent #2"
2 => "## Parent #2-1"
3 => "### Child #2-1-1"
4 => "## Parent #2-2"
5 => "### Child #2-2-1"
6 => "### Child #2-2-2"
7 => "## Parent #2-3"
8 => "## Parent #2-4"
9 => "# Grandparent #3"
10 => "## Parent #3-1"
]
К чему-то вроде этого
[
[
'name' => 'Grandparent #1'
],
[
'name' => 'Grandparent #2',
'children' => [
[
'name' => 'Parent #2-1',
'children' => [
[
'name' => 'Child #2-1-1',
]
]
],
[
'name' => 'Parent #2-2',
'children' => [
[
'name' => 'Child #2-2-1'
],
[
'name' => 'Child #2-2-2'
]
]
],
[
'name' => 'Parent #2-3',
],
[
'name' => 'Parent #2-4',
],
],
],
[
'name' 'Grandparent #3',
'children' => [
[
'name' => 'Parent #3-1'
]
]
]
]
Мой код:
Набор данных является минимально воспроизводимым примером.
Также может быть бесконечное количество #
после предыдущего.
2-Й хэштег (#2-1-1) используется для обеспечения ясности, а не для решения проблемы.
$array = [
"# Grandparent #1",
"# Grandparent #2",
"## Parent #2-1",
"### Child #2-1-1",
"## Parent #2-2",
"### Child #2-2-1",
"### Child #2-2-2",
"## Parent #2-3",
"## Parent #2-4",
"# Grandparent #3",
"## Parent #3-1",
];
function structure($lines, $target = 1) {
$data = [];
$parent = 0;
$i = 0;
foreach($lines as $line) {
$current = strlen(preg_split('/s /', $line)[0]);
if ($current == $target) {
$parent = $i;
$data[$parent]['name'] = $line;
}
if ($current != $target) {
$data[$parent]['children'][] = $line;
}
$i ;
}
// I tried placing structure function here again but it gives me errors
// structure($data[$parent]['children'], $target 1);
return $data;
}
$data = structure($array);
У меня работает прародитель, но, похоже, я не могу заставить его перейти к остальным. Я попытался поместить цикл в сам side, чтобы он выполнял поиск по другим дочерним элементам, но он будет выполняться бесконечно. И я не могу поместить foreach
внутри foreach
и так далее, потому что количество хэштегов может быть любой длины.
Ответ №1:
У меня не было много времени на изучение этого, но это то, что я придумал…
- цикл по массиву строк
- отделите последовательность символов хэша от имени элемента
- измерьте длину строки хэш-символа и сравните ее с текущим уровнем
- для каждого экземпляра «родительского элемента» на заданном уровне создайте ссылочную переменную и вставьте ее в выходной массив (дерево)
- каждый раз, когда вы сталкиваетесь с новым «родителем» на заданном уровне, старая ссылка должна быть уничтожена, а новая ссылка объявлена
- когда рекурсия необходима, передавайте ТОЛЬКО прямых потомков в рекурсивный вызов
- рекурсивный вызов должен быть условно вызван в конце цикла, чтобы обрабатывались необработанные потомки
Код: (Демо)
function recurse(array $lines, int $level = 1): array
{
$tree = [];
$directDescendants = [];
foreach ($lines as $line) {
[$hashes, $name] = explode(' ', $line, 2);
$depth = strlen($hashes);
if ($depth === $level) {
if ($directDescendants) {
$parent['children'] = recurse($directDescendants, $level 1);
}
$directDescendants = []; // reset collection of direct descendants
unset($parent); // destroy reference
$parent = ['name' => $name]; // name the member
$tree[] = amp;$parent; // push the reference variable into the tree
} elseif ($depth > $level) {
$directDescendants[] = $line; // collect only direct descendants of current level parent member
}
}
if ($directDescendants) {
$parent['children'] = recurse($directDescendants, $level 1);
}
return $tree;
}
var_export(recurse($array));