user3067352
user3067352

Reputation: 3

How to transform a simple array of file paths into a nested array representing a file hierarchy in PHP?

I need to write a function in PHP that takes the following input array:

$inputArray = [
[
    "path" => "/C",
    "basename" => "C",
    "size" => 4096,
    "modified" => 1540579748,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Recycle.Bin",
    "basename" => "Recycle.Bin",
    "size" => 0,
    "modified" => 1539012172,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Pictures",
    "basename" => "Pictures",
    "size" => 3164422144,
    "modified" => 1540581569,
    "type" => "file",
    "contents" => []
],
[
    "path" => "/C/Videos",
    "basename" => "Videos",
    "size" => 970752,
    "modified" => 1540579792,
    "type" => "file",
    "contents" => []
],
[
    "path" => "/C/Documents and Settings",
    "basename" => "Documents and Settings",
    "size" => 4096,
    "modified" => 1539022708,
    "type" => "link",
    "contents" => []
],
[
    "path" => "/C/Documents and Settings/Public",
    "basename" => "Public",
    "size" => 4096,
    "modified" => 1539012079,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Documents and Settings/desktop.ini",
    "basename" => "desktop.ini",
    "size" => 174,
    "modified" => 1506692592,
    "type" => "file",
    "contents" => []
],
[
    "path" => "/C/PerfLogs",
    "basename" => "PerfLogs",
    "size" => 0,
    "modified" => 1506692704,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Program Files",
    "basename" => "Program Files",
    "size" => 4096,
    "modified" => 1540579745,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Program Files (x86)",
    "basename" => "Program Files (x86)",
    "size" => 4096,
    "modified" => 1506692707,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/ProgramData",
    "basename" => "ProgramData",
    "size" => 4096,
    "modified" => 1539012665,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Recovery",
    "basename" => "Recovery",
    "size" => 0,
    "modified" => 1539022717,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/System Volume Information",
    "basename" => "System Volume Information",
    "size" => 4096,
    "modified" => 1539012967,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Users",
    "basename" => "Users",
    "size" => 4096,
    "modified" => 1539012384,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/Windows",
    "basename" => "Windows",
    "size" => 24576,
    "modified" => 1539011957,
    "type" => "dir",
    "contents" => []
],
[
    "path" => "/C/pagefile.sys",
    "basename" => "pagefile.sys",
    "size" => 1207959552,
    "modified" => 1540581479,
    "type" => "file",
    "contents" => []
],
[
    "path" => "/C/swapfile.sys",
    "basename" => "swapfile.sys",
    "size" => 268435456,
    "modified" => 1540581479,
    "type" => "file",
    "contents" => []
]
];

and produces the following output array:

$outputArray = [
[
    "path" => "/C",
    "basename" => "C",
    "size" => 4096,
    "modified" => 1540579748,
    "type" => "dir",
    "contents" => [
        [
            "path" => "/C/Recycle.Bin",
            "basename" => "Recycle.Bin",
            "size" => 0,
            "modified" => 1539012172,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Pictures",
            "basename" => "Pictures",
            "size" => 3164422144,
            "modified" => 1540581569,
            "type" => "file",
            "contents" => []
        ],
        [
            "path" => "/C/Videos",
            "basename" => "Videos",
            "size" => 970752,
            "modified" => 1540579792,
            "type" => "file",
            "contents" => []
        ],
        [
            "path" => "/C/Documents and Settings",
            "basename" => "Documents and Settings",
            "size" => 4096,
            "modified" => 1539022708,
            "type" => "link",
            "contents" => [
                [
                    "path" => "/C/Documents and Settings/Public",
                    "basename" => "Public",
                    "size" => 4096,
                    "modified" => 1539012079,
                    "type" => "dir",
                    "contents" => []
                ],
                [
                    "path" => "/C/Documents and Settings/desktop.ini",
                    "basename" => "desktop.ini",
                    "size" => 174,
                    "modified" => 1506692592,
                    "type" => "file",
                    "contents" => []
                ]
            ]
        ],
        [
            "path" => "/C/PerfLogs",
            "basename" => "PerfLogs",
            "size" => 0,
            "modified" => 1506692704,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Program Files",
            "basename" => "Program Files",
            "size" => 4096,
            "modified" => 1540579745,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Program Files (x86)",
            "basename" => "Program Files (x86)",
            "size" => 4096,
            "modified" => 1506692707,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/ProgramData",
            "basename" => "ProgramData",
            "size" => 4096,
            "modified" => 1539012665,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Recovery",
            "basename" => "Recovery",
            "size" => 0,
            "modified" => 1539022717,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/System Volume Information",
            "basename" => "System Volume Information",
            "size" => 4096,
            "modified" => 1539012967,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Users",
            "basename" => "Users",
            "size" => 4096,
            "modified" => 1539012384,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/Windows",
            "basename" => "Windows",
            "size" => 24576,
            "modified" => 1539011957,
            "type" => "dir",
            "contents" => []
        ],
        [
            "path" => "/C/pagefile.sys",
            "basename" => "pagefile.sys",
            "size" => 1207959552,
            "modified" => 1540581479,
            "type" => "file",
            "contents" => []
        ],
        [
            "path" => "/C/swapfile.sys",
            "basename" => "swapfile.sys",
            "size" => 268435456,
            "modified" => 1540581479,
            "type" => "file",
            "contents" => []
        ]
    ]
]
];

I understand that this can be done with recursion, but I'm having a hard time with it. If somebody could point me in the right direction or provide a working solution, that would be really cool.

Upvotes: 0

Views: 387

Answers (2)

Don't Panic
Don't Panic

Reputation: 41810

If you're able to use an array of objects instead, You can take advantage of the way objects are passed like references to simplify creating this kind of structure.

sort($inputArray);

foreach ($inputArray as $item) {
    $item = (object) $item;
    $files[$item->path] = $item;
    $files[substr($item->path, 0, strrpos($item->path, '/'))]->contents[] = $item;
}

$result = reset($files);

If objects won't work for you, JSON encode/decode can convert the objects in the result to arrays.

$result = json_decode(json_encode($result), true);

Upvotes: 1

pid
pid

Reputation: 11597

Sort the array by path if it's not already ordered:

usort($inputArray, function ($a, $b) {
    return $a['path'] <=> $b['path'];
});

Then nest the items recursively by path:

function nest(&$directory)
{
    for ($i = 0; $i < count($directory); $i++)
    {
        while ($i + 1 < count($directory) && strpos($directory[$i + 1]['path'], $directory[$i]['path'] . '/') === 0)
        {
            $directory[$i]['contents'][] = $directory[$i + 1];
            array_splice($directory, $i + 1, 1);
        }

        nest($directory[$i]['contents']);
    }
}

On case-insensitive filesystems (such as Windows) you may want to replace strpos() with stripos(), but I didn't test for this so I leave it to you to decide.

Upvotes: 1

Related Questions