Помогите преобразовать массив с помощью рекурсии
Начальный PHP-массив:
array (
0 =>
array (
'id_file' => '96',
'file_name' => 'Важный документ.docx',
'file_directory' => 'АК\\Документы',
'region' => 'Для работы',
'directory' => 'АК\\Документы\\Важный документ.docx',
),
1 =>
array (
'id_file' => '97',
'file_name' => 'июнь_2020.docx',
'file_directory' => 'АК\\Документы',
'region' => 'Для работы',
'directory' => 'АК\\Документы\\июнь_2020.docx',
),
2 =>
array (
'id_file' => '104',
'file_name' => '111222.pdf',
'file_directory' => 'АК\\Документы\\Почта',
'region' => 'Для работы',
'directory' => 'АК\\Документы\\Почта\\111222.pdf',
),
3 =>
array (
'id_file' => '110',
'file_name' => '111222sss.pdf',
'file_directory' => 'АК\\Документы\\Почта',
'region' => 'Для работы',
'directory' => 'АК\\Документы\\Почта\\111222sss.pdf',
),
4 =>
array (
'id_file' => '116',
'file_name' => 'asdasd.pdf',
'file_directory' => 'АК\\Документы\\УК',
'region' => 'Для работы',
'directory' => 'АК\\Документы\\УК\\asdasd.pdf',
),
5 =>
array (
'id_file' => '128',
'file_name' => '111222sss.pdf',
'file_directory' => 'ДК\\Картинки\\УК',
'region' => 'Для отдыха',
'directory' => 'ДК\\Картинки\\УК\\111222sss.pdf',
),
6 =>
array (
'id_file' => '128',
'file_name' => 'asdasd.pdf',
'file_directory' => 'АК\\Приказы',
'region' => 'Для работы',
'directory' => 'АК\\Приказы\\asdasd.pdf',
),
)
Нужно обработать рекурсией по имеющемуся пути [directory] , чтобы получилось следующее:
Array
(
[0] => Array
(
[id] => АК
[name] => АК
[type] => folder
[children] => Array
(
[id] => Для работы
[name] => Для работы
[type] => folder
[children] => Array
(
[id] => Документы
[name] => Документы
[type] => folder
[children] => Array
(
[0] => Array
(
[id] => 96
[name] => Важный документ.docx
[type] => file
[children] => Array
(
)
)
[1] => Array
(
[id] => 97
[name] => июнь_2020.docx
[type] => file
[children] => Array
(//если файл с расширением, то массив детей пуст
)
)
)
)
)
)
)
Все до чего дошел, это до обработки одной ссылки на документ:
function isFolder($value){
//если в строке присутствует расширение, возвращаем true
//в противном случае - это папка, и возвращаем false
if (!stripos($value, '.docx')){
return true;
}else{
return false;
}
}
$newArray=array();
foreach ($myArray as $k=>$values){
$newValue = explode('\\', $values['directory']);
$count = count($newValue);
if ($newArray[0]['name']!=$newValue[0]){ //если [directory] => АК - то все в массив [0] вставлять
$newArray[]=[ //строим дерево по ссылке которая имеется
'id'=>(isFolder($newValue[0]) ? $newValue[0] : $values['id_file']), //если это папка, то id => будет имя этой папки
'name'=>$newValue[0], //пишем имя
'type'=>isFolder($newValue[0]) ? 'folder' : 'file', //указываем, папка или файл
//а видимо уже включать рекурсию, для перечисления всех дочерних файлов/папок
//и заодно проверить [region]
'children'=>array(
//и кстати, если
'id'=>$newValue[1],
'name'=> $newValue[1],
'type'=>isFolder($newValue[1]) ? 'folder' : 'file',
'children'=>array(
//вывод всех файлов в директории
[
'id'=>(isFolder($newValue[2]) ? 'folder' : $values['id_file']), //выводим id файла
'name'=> $newValue[2],
'type'=>isFolder($newValue[2]) ? 'folder' : 'file',
'children'=> []
],
[
'id'=>(isFolder($newValue[2]) ? 'folder' : $values['id_file']),
'name'=> $newValue[2],
'type'=>isFolder($newValue[2]) ? 'folder' : 'file',
'children'=> []
]
),
),
];
}else{
//если [directory] => ДК - то строить уже новый массив [1]
}
}
Читал другие ответы/примеры/варианты, но как смонтировать тут рекурсию, никак понять не могу. т.к. есть пара подводных камней:
- Начальный массив всегда должен иметь в начале [0] либо [1] -> к нему все остальные child (что в принципе логично, т.к. родительская папка всегда имеет имя АК либо ДК.
- обязательно необходимо сделать прослойку вида АК\[region]\Документы\Важный документ.docx и тут уже [region] является родителем всех остальных т.е. АК\[region]\Приказы\asdasd.docx.
- ID в виде родитель-ребенок тоже сложны (и в реализации в принципе не нуждается), т.к. если получается type => folder то ID будет являться имя папки. а если уже файл - то ID файла.
Может кто-нибудь помочь с обработкой рекурсии? Заранее благодарен.
[children] => Array(
[id] => АК
[name] => АК
[type] => folder
[children] => Array(вложенность неизвестной длинны))
Ответы (2 шт):
Автор решения: vp_arth
→ Ссылка
Простое дерево собирается циклом, вот так:
$list = array_map(function($item){
$path = explode('\\', $item['file_directory']);
array_splice($path, 1, 0, $item['region']); // [region] :)
return array(
'id' => $item['id_file'],
'path' => $path,
'name' => $item['file_name'],
);
}, $inputArray);
$tree = [];
foreach ($list as $item) {
$resPt = &$tree;
foreach ($item['path'] as $pathPart) {
if (!array_key_exists($pathPart, $resPt)) {
$resPt[$pathPart] = [];
}
$resPt = &$resPt[$pathPart];
}
$resPt['path'] = implode('\\', $item['path']);
$resPt['files'][] = array('id' => $item['id'], 'name' => $item['name']);
unset($resPt);
}
Навтыкать children'ов, при желании, не проблема)
Вывернуть цикл в рекурсию, конечно, можно.. Но зачем?
Автор решения: vp_arth
→ Ссылка
Рекурсивная обработка списка.
Рекурсия запускается с полной копией оригинального списка, из которого отсекается всё лишнее по префиксу.
$list = array_map(function($item){
$path = explode('\\', $item['file_directory']);
array_splice($path, 1, 0, $item['region']);
return array(
'id' => $item['id_file'],
'path' => $path,
'name' => $item['file_name'],
);
}, $inputArray);
function buildTree($items, $prefix = []) {
$res = [];
$visited = [];
foreach($items as $item) {
if (count($item['path']) < count($prefix)) continue;
if (array_slice($item['path'], 0, count($prefix)) !== $prefix) continue;
$node = [];
if (count($item['path']) > count($prefix)) {
$curPath = $item['path'][count($prefix)];
$nextPrefix = array_merge($prefix, [$curPath]);
$strPrefix = implode('\\', $nextPrefix);
if ($visited[$strPrefix] ?? false) continue; // префикс обработан?
$node['type'] = 'folder';
$node['path'] = $strPrefix;
$node['name'] = $curPath;
$node['children'] = buildTree($items, $nextPrefix); // запускаем рекурсию
$visited[$strPrefix] = true; // префикс обработан
} else {
$node['type'] = 'file';
$node['name'] = $item['name'];
}
$res[] = $node;
}
return $res;
}
$tree = buildTree($list);