У меня есть такой массив:
$arr = array(
'home.js' => new File(),
'view/index.html' => new File(),
'src/index.js' => new File(),
'src/libs/jquery.js' => new File()
);
Теперь я хочу преобразовать в такую структуру:
Array
(
[0] => Array
(
[text] => home.js
)
[1] => Array
(
[text] => view
[children] => Array
(
[0] => Array
(
[text] => index.html
)
)
)
[2] => Array
(
[text] => src
[children] => Array
(
[0] => Array
(
[text] => index.js
)
[1] => Array
(
[text] => libs
[children] => Array
(
[0] => Array
(
[text] => jquery.js
)
)
)
)
)
)
Я пытался часами, с помощью ответов StackOverfow, но я не мог найти решение, так как все другие вопросы имеют другую настройку.
Редактировать:
То, что я получил до сих пор с помощью SO, — это (хотя не могу вспомнить точный ответ):
$out = array();
foreach($arr as $path => $file) {
$parts = explode('/', trim($path, '/'));
applyChain($out, $parts, $file);
}
function applyChain(&$arr, $parts, $value)
{
if (!is_array($parts)) {
return;
}
if (count($parts) == 0) {
$arr = $value;
} else {
array_shift($parts);
applyChain($arr[], $parts, $value);
}
}
print_r($out);
Я не знаю, как именно это работает, особенно часть applyChain($arr[] ...
). Это вроде работает с глубиной, но не с именами файлов. Я получаю следующий вывод:
Array
(
[0] => File Object
(
)
[1] => Array
(
[0] => File Object
(
)
)
[2] => Array
(
[0] => File Object
(
)
)
[3] => Array
(
[0] => Array
(
[0] => File Object
(
)
)
)
)
Там будет решение в несколько строк, используя explode()
а также eval()
, Но eval()
не считается чистым, поэтому давайте попробуем рекурсию:
<?php
class File {
}
$arr = array(
'home.js' => new File(),
'view/index.html' => new File(),
'src/index.js' => new File(),
'src/libs/jquery.js' => new File()
);
function sub($path) {
$rv = array();
$parts = explode('/', $path, 2); // strip off one level
$rv['text'] = $parts[0]; // put it into 'text' element
if (count($parts)>1) // is there anything left?
$rv['children'] = sub($parts[1]); // do the same for the rest of the path
return $rv;
}
$new = array();
foreach (array_keys($arr) as $file) {
$new[] = sub($file);
}
var_dump($new);
?>
Но, как прокомментировал Питер, это создает отдельные подструктуры, даже если пути имеют некоторую общую часть (например, src/libs/jquery.js
а также src/libs/melon.js
).
С использованием уродливых eval()
(который можно заменить позже) я получил следующий код:
<?php
class File {
}
$arr = array(
'home.js' => new File(),
'view/index.html' => new File(),
'src/index.js' => new File(),
'src/libs/jquery.js' => new File(),
'src/libs/melon.js' => new File(),
);
// conversion
function sub($element) {
$rv = array();
foreach (array_keys($element) as $sub) {
$part['text'] = $sub;
if (is_array($element[$sub])) {
$part['children'] = sub($element[$sub]);
}
$rv[] = $part;
}
return $rv;
}
// create array with path file/folder names as keys
$new = array();
foreach (array_keys($arr) as $row) {
$def = '$new["'.preg_replace('&/&', '"]["', $row).'"] = 1;';
eval($def);
}
// run
$new2 = sub($new);
var_dump($new2);
?>
Это выводы
array(3) {
[0]=>
array(1) {
["text"]=>
string(7) "home.js"}
[1]=>
array(2) {
["text"]=>
string(4) "view"["children"]=>
array(1) {
[0]=>
array(1) {
["text"]=>
string(10) "index.html"}
}
}
[2]=>
array(2) {
["text"]=>
string(3) "src"["children"]=>
array(2) {
[0]=>
array(1) {
["text"]=>
string(8) "index.js"}
[1]=>
array(2) {
["text"]=>
string(4) "libs"["children"]=>
array(2) {
[0]=>
array(1) {
["text"]=>
string(9) "jquery.js"}
[1]=>
array(1) {
["text"]=>
string(8) "melon.js"}
}
}
}
}
}
Других решений пока нет …