I want to get an array of immediate sub-directories based on the current path from a given list of paths. Here is an example list of paths and the desired result:
$dirs = [
'/chris',
'/chris/usa',
'/david',
'/',
'/chris/canada/2022',
'/david/uk',
];
$current_path = "/chris";
// Required result:
$sub_dirs = ['/usa', '/canada'];
The current path could be anything from /
to the last sub directory, in which case I would end up with an empty $sub_dirs
.
My best attempt at it is this:
$dirs = [
'/chris',
'/chris/usa',
'/david',
'/',
'/chris/canada/2022',
'/david/uk',
];
$sub_dirs = [];
$current_path = "/";
foreach($dirs as $dir){
if(strstr($dir, $current_path)){
$sub_dirs[] = str_replace($current_path, '', $dir);
}
}
The above fails at two things. If the $current_path = "/"
then the returned array is just paths without slashes since I strip them out with strstr()
and if the $current_path = "/chris"
then I still get the sub-directories.
How should I correctly solve it? Thank you!
CodePudding user response:
- You can search for
/chris/
to get only sub directories. - To get only the first directory of matching paths, you could use
explode()
, and get the first. - As of PHP8, you can use
str_starts_with()
$dirs = ['/chris', '/chris/usa', '/david', '/', '/chris/canada/2022', '/david/uk'];
$current_path = "/";
$sub_dirs = [];
foreach($dirs as $dir)
{
// Ensure $dir starts with path '/' (sub dir of $current_path)
if (!str_starts_with($dir, $current_path)) {
continue;
}
// Get the relative path from $current_path
$relPath = substr($dir, strlen($current_path));
// Get the first dir of this path
[$dir2] = explode('/', ltrim($relPath,'/'));
// skip same directory
if (empty($dir2)) {
continue;
}
// Store and add removed slash
$sub_dirs[] = '/' . $dir2;
}
var_export(array_unique($sub_dirs));
Output
array (
0 => '/usa',
1 => '/canada',
)
CodePudding user response:
I like the approach of creating a tree out of the paths (adapted from this answer). Then it's easier to get the children based on a path.
function buildTree($paths)
{
// Create a hierarchy where keys are the labels
$rootChildren = [];
foreach ($paths as $path) {
$branch = explode("/", $path);
$children = &$rootChildren;
foreach ($branch as $label) {
if ($label) {
if (!isset($children[$label])) {
$children[$label] = [];
}
$children = &$children[$label];
}
}
}
// Create target structure from that hierarchy
function recur($children)
{
$result = [];
foreach ($children as $label => $grandchildren) {
$result[$label] = recur($grandchildren);
}
return $result;
}
return recur($rootChildren);
}
function findChildren($tree, $path)
{
$branch = explode("/", $path);
$pointer = $tree;
foreach ($branch as $label) {
if ($label) {
$pointer = $pointer[$label];
}
}
return array_keys($pointer);
}
$dirs = [
'/chris',
'/chris/usa',
'/david',
'/',
'/chris/canada/2022',
'/david/uk',
];
$tree = buildTree($dirs);
print_r($tree);
$current_path = "/chris";
print_r(findChildren($tree, $current_path));
Output:
Array
(
[chris] => Array
(
[usa] => Array
(
)
[canada] => Array
(
[2022] => Array
(
)
)
)
[david] => Array
(
[uk] => Array
(
)
)
)
Array
(
[0] => usa
[1] => canada
)
CodePudding user response:
I feel like I have a bit shorter answer than the previous ones, but not sure if it's the most efficient one though.
$dirs = [
'/chris',
'/chris/usa',
'/david',
'/',
'/chris/canada/2022',
'/david/uk',
];
$current_path = "/chris";
$sub_dirs = array_map(function($s) { return '/'.explode("/", $s)[2]; },array_filter($dirs, function($dir) use ($current_path) {
return substr($dir, 0, strlen($current_path) 1) === $current_path . '/';
}));
print_r($sub_dirs); // Output: Array ( [1] => /usa [4] => /canada )