Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Multidimensional array directory map

I'm trying to get a directory structure in a multidimensional array.

I got this far:

function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
    if (!$dir instanceof DirectoryIterator) {
        $dir = new DirectoryIterator((string) $dir);
    }
    $dirs = array();
    $files = array();
    foreach ($dir as $node) {
        if ($node->isDir() && !$node->isDot()) {
            $tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
            if (!$ignoreEmpty || count($tree)) {
                $dirs[$node->getFilename()] = $tree;
            }
        } elseif ($node->isFile()) {
            $name = $node->getFilename();
            if ('' == $regex || preg_match($regex, $name)) {
                $files[] = $name;
            }
        }
    }
    asort($dirs);
    sort($files);
    return array_merge($dirs, $files);
}

But I am having issues getting the folder name instead of the index 0,1 .etc. This seems to be due to the fact that my directories have numeric names?

Array
(
    [0] => Array  // 0 should be the folder name
        (
            [0] => m_109225488_1.jpg
            [1] => t_109225488_1.jpg
        )

    [1] => Array
        (
            [0] => m_252543961_1.jpg
            [1] => t_252543961_1.jpg
        )
like image 413
Alex Avatar asked Mar 29 '26 20:03

Alex


2 Answers

The solution was rather simple thanks to: Merge array without loss key index

Instead of array_merge simply do $dirs + $files

Potential solution (potential issue point out by Roger Gee):

function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
    if (!$dir instanceof DirectoryIterator) {
        $dir = new DirectoryIterator((string) $dir);
    }
    $dirs = array();
    $files = array();
    foreach ($dir as $node) {
        if ($node->isDir() && !$node->isDot()) {
            $tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
            if (!$ignoreEmpty || count($tree)) {
                $dirs[$node->getFilename()] = $tree;
            }
        } elseif ($node->isFile()) {
            $name = $node->getFilename();
            if ('' == $regex || preg_match($regex, $name)) {
                $files[] = $name;
            }
        }
    }
    return $dirs + $files;
}

Better solution?

function dirtree($dir, $regex = '', $ignoreEmpty = false)
{
    if (!$dir instanceof DirectoryIterator) {
        $dir = new DirectoryIterator((string) $dir);
    }
    $filedata = array();
    foreach ($dir as $node) {
        if ($node->isDir() && !$node->isDot()) {
            $tree = dirtree($node->getPathname(), $regex, $ignoreEmpty);
            if (!$ignoreEmpty || count($tree)) {
                $filedata[$node->getFilename()] = $tree;
            }
        } elseif ($node->isFile()) {
            $name = $node->getFilename();
            if ('' == $regex || preg_match($regex, $name)) {
                $filedata[] = $name;
            }
        }
    }
    return $filedata;
}
like image 185
Alex Avatar answered Mar 31 '26 09:03

Alex


Using the array union operation is dangerous since you can potentially overwrite existing files. Consider the following directory structure:

a       <-- directory
├── 0   <-- directory (empty)
├── b   <-- regular file
└── c   <-- directory
    └── d   <-- regular file

Now consider running the operation using the array union. I get the following result:

array(2) {
  [0]=>
  array(0) {
  }
  ["c"]=>
  array(1) {
    [0]=>
    string(1) "d"
  }
}

Notice how regular file b is not present? This is because the array union operation prefers the existing 0 index over the 0 index from the right operand (which contains the regular files).

I would stick with the original implementation present in the question or use a special bucket for files that doesn't contain a valid filesystem name (e.g. :files:). Note that this may be platform-specific as to what you choose.

In the case of the original implementation, you can decide whether the index is a directory vs regular file by calling is_array or is_scalar on the value. Note that since the directories array is the first parameter to array_merge, you are guaranteed that no directory indexes get incremented and will always refer to the correct directory names.

Here's how you could determine just the directory names:

function getDirectoryNames($result) {
    $ds = [];
    foreach ($result as $key => $value) {
        if (is_array($value)) {
            $ds[] = $key;
        }
    }
    return $ds;
}
like image 45
Roger Gee Avatar answered Mar 31 '26 08:03

Roger Gee



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!