skip to Main Content

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!

3

Answers


    • 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',
    )
    
    Login or Signup to reply.
  1. 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
    )
    
    Login or Signup to reply.
  2. 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 )
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search