Создание иерархического многомерного массива на основе количества последовательных символов в начале строк

#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:

У меня не было много времени на изучение этого, но это то, что я придумал…

  1. цикл по массиву строк
  2. отделите последовательность символов хэша от имени элемента
  3. измерьте длину строки хэш-символа и сравните ее с текущим уровнем
  4. для каждого экземпляра «родительского элемента» на заданном уровне создайте ссылочную переменную и вставьте ее в выходной массив (дерево)
  5. каждый раз, когда вы сталкиваетесь с новым «родителем» на заданном уровне, старая ссылка должна быть уничтожена, а новая ссылка объявлена
  6. когда рекурсия необходима, передавайте ТОЛЬКО прямых потомков в рекурсивный вызов
  7. рекурсивный вызов должен быть условно вызван в конце цикла, чтобы обрабатывались необработанные потомки

Код: (Демо)

 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));