How do i convert multidimensional array to file path.
I have this array :
$data = [
"users" => [
"joe" => [
"photos" => ["a.jpg","b.jpg"],
"files" => ["a.doc","b.doc"]
],
"annie" => [
"photos" => ["a.jpg","b.jpg"],
"files" => ["a.doc","b.doc"]
],
]
];
that i must convert to path example :
"users/joe/photos/a.jpg";
"users/joe/photos/b.jpg";
"users/joe/files/a.doc";
"users/joe/files/b.doc";
"users/annie/photos/a.jpg";
"users/annie/photos/b.jpg";
"users/annie/files/a.doc";
"users/annie/files/b.doc";
But i can't have the best result with this functions :
$path = "";
function iterate($data, $path)
{
echo "<br>";
foreach ($data as $key => $item){
if (is_array($item)){
$path .= $key.DIRECTORY_SEPARATOR;
iterate($item, $path);
}else{
echo $path.$item."<br>";
}
}
}
output :
users/joe/photos/a.jpg
users/joe/photos/b.jpg
users/joe/photos/files/a.doc
users/joe/photos/files/b.doc
users/joe/annie/photos/a.jpg
users/joe/annie/photos/b.jpg
users/joe/annie/photos/files/a.doc
users/joe/annie/photos/files/b.doc
Please help.
Thanks
You could make use of RecursiveIteratorIterator + RecursiveArrayIterator:
function computeFilePaths(array $fileTree): array
{
$filePaths = [];
$iterator = new RecursiveIteratorIterator(new RecursiveArrayIterator($fileTree));
foreach ($iterator as $fileName) {
for ($folders = [], $pos = 0, $depth = $iterator->getDepth(); $pos < $depth; $pos++) {
$folders[] = $iterator->getSubIterator($pos)->key();
}
$filePaths[] = implode('/', $folders) . '/' . $fileName;
}
return $filePaths;
}
print_r(computeFilePaths($yourArrayGoesHere));
I highly suggest this question's selected answer to understand how these iterators work.
Related
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!
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',
)
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
)
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 )
I have a api that returns requested images paths to front end. But in Linux server its needs natural sorting. I want to natural sort values in the yol key.
$klein->respond('GET', '/getPaths/[:name]', function($request,$response) {
$path00 = "../manga/" . $request->name;
function getAll($path00)
{
$dirs = [];
foreach (new DirectoryIterator($path00) as $item) {
if (!$item->isDir() || $item->isDot()) {
continue;
}
$gedo = [];
$path01 = $path00 . "/" . $item;
$path02 = substr($path01, 3);
$yol = new DirectoryIterator($path01);
foreach ($yol as $esya) {
if (!$esya->isDot() && $esya->getFilename() !== ".DS_Store") {
$gedo[] = $path02 . "/" . $esya->getFilename();
}
}
$dirs[] = array('klasor' => $item->getFilename(), 'yol' => $gedo);
}
return $dirs; // Output its in the image.
};
$data = getAll($path00);
$response->json($data);
});
Output:
Take a look at PHP sort functions. natsort should work for your case.
Simply change the following line to this:
natsort($gedo);
$dirs[] = array('klasor' => $item->getFilename(), 'yol' => $gedo);
Also a simple code example:
$input = [
'abc' => [
'item1',
'item4',
'item2'
]
];
$output = natsort($input['abc']); // $output will be true
// but $input is now sorted and looking like:
// 'abc' => [
// 'item1',
// 'item2',
// 'item4'
// ]
I created a configuration provider that process configuration in yaml file for silex.
Everything is ok ! even import feature work great.
the only problem that I have is with dynamic replacement for parameters surrounded with %% parameters.
base_path: /my/path/to/base
paths:
web_path: %base_path%/web
upload_path: %web_path%/uploads
I don't really know if the Symfony Configuration component can process the data this way.
May be this code will help you
function _flattenArray($array, &$flatten, $index = null, $path = null)
{
if (\is_array($array)) {
foreach ($array as $k => $v) {
_flattenArray(
$v,
$flatten,
(isset($index) ? $index . '.' : '') . $k,
(isset($path) ? $path : '') . '[' . $k . ']'
);
}
} else {
$flatten[$index] = [
'path' => $path,
'value' => $array,
];
}
}
$config = ...; // load config
$flatten = [];
_flattenArray($config, $flatten);
$parameterBag = [];
foreach ($flatten as $k => $v) {
$parameterBag[$k] = $v['value'];
}
$parameterBag = new \Symfony\Component\DependencyInjection\ParameterBag\ParameterBag($parameterBag);
$parameterBag->resolve();
$parameterBag = $parameterBag->all();
$propertyAccessor = \Symfony\Component\PropertyAccess\PropertyAccess::createPropertyAccessor();
foreach ($parameterBag as $k => $v) {
if ($v === $flatten[$k]['value']) continue;
$propertyAccessor->setValue($config, $flatten[$k]['path'], $v);
}
dump($config);
I am trying to get multi-dimensional array for directories formatted as below :
[
{
"text": "another_folder",
"href": "gui\/default\/uploads\/another_folder",
"depth": 0
},
{
"text": "subfold",
"href": "gui\/default\/uploads\/subfold",
"depth": 0,
"nodes": {
"text": "sub-subfold",
"href": "gui\/default\/uploads\/subfold\/sub-subfold",
"depth": 1,
}
}
]
I want to use RecursiveIterators. What I have done so far is I am getting all directories listed in given path. I need to go inside to children which is where I stacked.
public function list_folders($folder_path='') {
if(!$folder_path) $folder_path = $this->upl_path;
$iterator = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($folder_path),
RecursiveIteratorIterator::SELF_FIRST);
$iterator->setFlags(RecursiveDirectoryIterator::SKIP_DOTS);
$r = array();
$counter = 0
foreach ($iterator as $splFileInfo) {
if($splFileInfo->isDir()) {
$r[$counter] = array(
'text' => $splFileInfo->getFilename(),
'href' => str_replace('\\','/',$splFileInfo->getPathname())
);
if(How to check if it has children) {
$result[$counter] += array('nodes'=> CALL RECURSIVE HERE ? );
}
$counter++;
}
echo json_encode($r,JSON_PRETTY_PRINT);
}
I'd use any idea or help gladly.
Your code was almost functional, but it was missing a few key points. I adapted your code so that it works, and added some comments that I hope will help you understand what you were missing:
class FolderListing
{
public function list_folders($folder_path = '', $depth = 0)
{
if (!$folder_path) $folder_path = $this->upl_path;
$iterator = new IteratorIterator(new DirectoryIterator($folder_path));
$r = array();
foreach ($iterator as $splFileInfo) {
if ($splFileInfo->isDot()) {
continue;
}
// we need to do this for both folders and files
$info = array(
'text' => $splFileInfo->getFilename(),
'href' => str_replace('\\', '/', $splFileInfo->getPathname()),
'depth' => $depth
);
// is we have a directory, try and get its children
if ($splFileInfo->isDir()) {
// !!! I recommend to do an echo $splFileInfo->getPathname() here
// to see the order in which recursion works !!!
$nodes = $this->list_folders($splFileInfo->getPathname(), $depth + 1);
// only add the nodes if we have some
if (!empty($nodes)) {
$info['nodes'] = $nodes;
}
}
// add the info to the array. No need for a counter :)
$r[] = $info;
}
// the return is important to be able to build the multi dimensional array
return $r;
}
}
$test = new FolderListing();
$ret = $test->list_folders('./test'); // change this to whatever you want
var_dump($ret);
Good luck!
'I have this flat array:
$folders = [
'test/something.txt',
'test/hello.txt',
'test/another-folder/myfile.txt',
'test/another-folder/kamil.txt',
'test/another-folder/john/hi.txt'
]
And I need it in the following format:
$folders = [
'test' => [
'something.txt',
'hello.txt',
'another-folder' => [
'myfile.txt',
'kamil.txt',
'john' => [
'hi.txt'
]
]
]
];
How do I do this? Thanks.
Recursion is your friend :-)
function createArray($folders, $output){
if(count($folders) > 2){
$key = array_shift($folders);
$output[$key] = createArray(
$folders, isset($output[$key]) ? $output[$key] : []
);
}
else{
if(!isset($output[$folders[0]])){
$output[$folders[0]] = [];
}
$output[$folders[0]][] = $folders[1];
}
return $output;
}
Keep drilling down until you get to the file name, then add them all together in an array.
You need to call this function for each element in your array, like this:
$newFolders = [];
foreach($folders as $folder){
$newFolders = createArray(explode('/', $folder), $newFolders);
}
DEMO: https://eval.in/139240
<?php
$folders = [
'test/something.txt',
'test/hello.txt',
'test/another-folder/myfile.txt',
'test/another-folder/kamil.txt',
'test/another-folder/john/hi.txt'
];
$new_folders = array();
foreach ($folders as $folder) {
$reference =& $new_folders;
$parts = explode('/', $folder);
$file = array_pop($parts);
foreach ($parts as $part) {
if(!isset($reference[$part])) {
$reference[$part] = [];
}
$reference =& $reference[$part];
}
$reference[] = $file;
}
var_dump($new_folders);