I create a function for remove child dirs on arrays structure. USer for error or mistake, has put on scenario, subdirs child of a parent dir already in config list.
/dir_a/subdir_a
/dir_b/subdir_a/subdir_a
/dir_b/subdir_a/subdir_b
/dir_b/
/dir_c/subdir_a/
...
Code
$local_pre_order = array_unique($local_sync);
asort($local_pre_order);
$local_order = array();
foreach ($local_pre_order as $value)
{
$repeat = false;
foreach ($local_pre_order as $value2)
{
$pos = strpos($value,$value2);
if (($pos !== false ) && ($value != $value2) && ($pos == 0)) {
$repeat = true;
break;
}
}
if (!$repeat) {
$local_order[] = $value;
}
}
Sort OK
/dir_a/subdir_a
/dir_b/
/dir_c/subdir_a/
I think it's a no good programing. Work but not fine. IMHO. Any ideas for best code?
Sorting is the way to go. Note that when sorted, all the child subdirs will be immediatelly after their parents. So it's just a matter of avoiding adding subdirs if the last added dir has a common prefix with it.
PHP isn't my first language, so please don't mind this non-idiomatic code.
$local_sync = [
"/dir_a/subdir_a",
"/dir_b/subdir_a/subdir_a",
"/dir_b/subdir_a/subdir_b",
"/dir_b/",
"/dir_c/subdir_a/"
];
$copy = $local_sync;
asort($copy);
$output = array();
foreach ($copy as $value)
if (strpos($value, end($output)) !== 0)
$output[] = $value;
print_r($output)
It's best not to rely on the array's structure, don't rely on the array being sorted or a comparison between the current path and the previous. What you want to do is use dirname () to get each path's directory name, and search the output array for each path, only adding the path to the output array if it's not already in the output array.
Like so...
$directory_paths = array (
'/dir_a/subdir_a',
'/dir_b/subdir_a/subdir_a',
'/dir_b/subdir_a/subdir_b',
'/dir_b/',
'/dir_c/subdir_a/'
);
$sorted_paths = $directory_paths;
asort ($sorted_paths);
$output = array ();
foreach ($sorted_paths as $key => $path) {
$directory = $path;
$levels = substr_count (trim ($path, '/'), '/') - 1;
for ($i = 0; $i < $levels; $i++) {
$directory = dirname ($directory);
}
if (in_array ($directory, $sorted_paths)) {
$output[$key] = $path;
}
}
ksort ($output);
$output = array_values ($output);
echo print_r ($output, true);
Related
I wrote this code a long time ago to get files from a folder structure given in $dir.
$recursiveIterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::CHILD_FIRST);
$ritit = new RegexIterator($recursiveIterator, $filter);
foreach ($ritit as $splFileInfo) {
if(($splFileInfo->getFileName() != ".") && ($splFileInfo->getFileName() != "..")) {
$path = $splFileInfo->isDir()
? array($splFileInfo->getFilename() => array())
: array($splFileInfo->getFilename());
for ($depth = $ritit->getDepth() - 1; $depth >= 0; $depth--) {
$path = array($ritit->getSubIterator($depth)->current()->getFilename() => $path);
}
$return = array_merge_recursive($return, $path);
}
}
And as the title suggests, I want the $return array to have the folders first. I first attempted to correct this with a foreach after the loop, and sort into $folders and $files array, however this wouldnt change the contents inside the folders, if there were mutliple children inside children.
Is there a way to modify the above loop so that all folders appear first in the array and files after? Including children and children's children?
I don't think that you can modify the loop to get the output array the way you want it. Instead, I'd rather use recursive sorting function to sort the array after the loop.
First, create function that defines the logic for sorting elements. In your case, you want the array-type elements to be the first elements in a tier, so the sorting function could look like this:
function dirFirstSorting($a, $b)
{
if (is_array($a) && is_string($b)) {
return -1;
} elseif (is_string($a) && is_array($b)) {
return 1;
} else {
return 0;
}
}
Then, create a function that recursively sorts elements in array:
function sortFilesListing(&$array)
{
if (is_array($array)) {
uasort($array, "dirFirstSorting");
array_walk($array, "sortFilesListing");
}
}
All you need to do now, is to call sortFilesListing function with $return array provided:
sortFilesListing($return);
The $return array elements should be now sorted accordingly.
I have tried for a long time but couldn't find a way to merge an array in to a new one.
Mostly I get lost in looping and matching.;(
I would like to recieve a php 5 method that can do the following:
Example 1
Lets say there is an array with url's like:
Array(
'a',
'a/b/c',
'a/b/c/d/e',
'a/y',
'b/z',
'b/z/q/',
)
Every last folder of the url's is the folder where a user has the right to view.
I would like to send the array to a method that returns a new array like:
Array[](
'a/c/e'
'a/y'
'z/q'
)
The method has combined some elements of the origninal array into one element.
This because there is a match in allowed ending folders.
Example 2
Array(
'projects/projectA/books'
'projects/projectA/books/cooking/book1'
'projects/projectA/walls/wall'
'projects/projectX/walls/wall'
'projects/projectZ/'
'projects/projectZ/Wood/Cheese/Bacon'
)
I would like to get a an array like:
Array[](
'books/book1'
'wall'
'wall'
'projectZ/Bacon'
)
Then it would be great (specialy in case of the 'wall' values) to have some references to the full path's of the original array.
Do it like below:-
<?php
$array = Array(
'projects/projectA/books',
'projects/projectA/books/cooking/book1',
'projects/projectA/walls/wall',
'projects/projectX/walls/wall',
'projects/projectZ/',
'projects/projectZ/Wood/Cheese/Bacon'
);// original array
$final_array =array(); // new array variable
foreach($array as $key=>$arr){ // iterate over original array
$exploded_string = end(array_filter(explode('/',$arr))); // get last-value from the url string
foreach($array as $ar){ // iterate again the original array to compare this string withh each array element
$new_exploded_string = end(array_filter(explode('/',$ar))); // get the new-last-values from url string again
if($arr !== $ar && strpos($ar,$exploded_string) !==false){ // if both old and new url strings are not equal and old-last-value find into url string
if($exploded_string == $new_exploded_string ){ // if both new-last-value and old-last-value are equal
$final_array[] = $exploded_string;
}else{
$final_array[] = $exploded_string.'/'.$new_exploded_string ;
}
}
}
}
print_r($final_array);
Output:-https://eval.in/846738
Well, there isn't a single built-in function for this ;)
$items = array(
'projects/projectA/books',
'projects/projectA/books/cooking/book1',
'projects/projectA/walls/wall',
'projects/projectX/walls/wall',
'projects/projectZ/',
'projects/projectZ/Wood/Cheese/Bacon',
'hold/mold/gold/sold/fold',
'hold/mold/gold',
'raja/maza/saza',
'raja/maza',
'mohit/yenky/client/project',
);
echo '$items = ' . nl2br(htmlspecialchars(print_r($items, true))); //Debug
// Sort, so the shorter basePath comes before the longer subPath
usort($items, function($a, $b) {
if (strlen($a) == strlen($b)) {
return 0;
} else {
return strlen($a) > strlen($b) ? 1 : -1;
}
});
$result = array();
while($basePath = array_shift($items)) { // As long as there is a next item
$basePath = rtrim($basePath, '/'); // Right trim extra /
foreach($items as $idx => $subPath) {
if (strpos($subPath, $basePath . '/') === 0) {
// $subPath begins with $basePath
$result[] = preg_replace('#.*/#', '', $basePath) . '/' . preg_replace('#.*/#', '', rtrim($subPath, '/'));
unset($items[$idx]); // Remove item from array, so it won't be matched again
continue 2; // Continue with next while($basePath = array_shift($items))
}
}
// No subPath found, otherwise continue would have called (skipping below code)
$result[] = preg_replace('#.*/#', '', $basePath);
}
echo '$result = ' . nl2br(htmlspecialchars(print_r($result, true))); //Debug
PHPFiddle: http://phpfiddle.org/main/code/ugq9-hy0i
You can avoid using nested loops (and, actually, you should avoid):
sort($array);
$carry = array_shift($array);
$result = [];
$i = 0;
$lastItem = array_reduce($array, function ($carry, $item) use (&$result, &$i) {
$result[$i] = isset($result[$i])
? array_merge($result[$i], [basename($carry)])
: [basename($carry)];
if (strpos($item, $carry) !== 0) {
$i += 1;
}
return $item;
}, $carry);
if (!empty($lastItem)) {
$result[$i] = isset($result[$i])
? array_merge($result[$i], [basename($lastItem)])
: [basename($lastItem)];
}
$result = array_map(function ($item) {
return implode('/', $item);
}, $result);
Here is working demo.
We use array_reduce here to get access to the previously processed item. Also, PHP has function basename, that retrieves the basename. So you can use it and do not reinvent the wheel.
I'm working on the following but have become stumped as to how to get this to output.
I have the following which scans the directory contents, then gets the info and saves it as an array:
//SCAN THE DIRECTORY
$directories = scandir($dir);
$directinfo = array();
foreach($directories as $directory){
if ($directory === '.' or $directory === '..') continue;
if(!stat($dir.'/'.$directory)){
} else {
$filestat = stat($dir.'/'.$directory);
$directinfo[] = array(
'name' => $directory,
'modtime' => $filestat['mtime'],
'size' => $filestat['size']
);
}
}
When trying to output it however, I'm just getting single letters with a lot of breaks. Im obviously missing something here with the output loop.
foreach($directinfo as $dirInfo){
foreach($dirInfo as $drInfo){
for ($x=0; $x<=2; $x++) {
<span>"".$drInfo[$x]."<br/></span>";
}
}
}
Help is greatly appreciated. :)
You have already did everything just remove your for loop.
and try to do the following-
foreach($directinfo as $dirInfo){
foreach($dirInfo as $key=>$drInfo){
echo "<span>".$key."=>".$drInfo."<br/></span>";
}
}
I think your dealing with a 2d array, but treating it like a 3d array.
what does
foreach($directinfo as $dirInfo){
foreach($dirInfo as $drInfo){
var_dump($drInfo);
}
}
give you?
You're building a single array, dirInfo.
Php foreach takes the array first;
foreach($dirInfo as $info) {
echo "<span>" . $info['name'] . "</span>";
}
Try this function. It will return you list of all files with path.
// to list the directory structure with all sub folders and files
function getFilesList($dir)
{
$result = array();
$root = scandir($dir);
foreach($root as $value) {
if($value === '.' || $value === '..') {
continue;
}
if(is_file("$dir$value")) {
$result[] = "$dir$value";
continue;
}
if(is_dir("$dir$value")) {
$result[] = "$dir$value/";
}
foreach(getFilesList("$dir$value/") as $value)
{
$result[] = $value;
}
}
return $result;
}
I have several files uploaded with a form that is processed by a PHP script. There are several "categories" that I need to have handles separate ways. In other words, I need some of the files in the $_FILES array to be stored someplace, and some others stored in other places.
So what I need is all files (array keys) that are not $_FILES['filename_form'] to be stored / used separably.
The reason why I need this is because the amount of files being uploaded are dynamic, but always either $_FILES['filename_type1'] or $_FILES['filename_type2_*] where * is a number that increments when more files are input from the form (dynamically)
Might be a very simple solution, either I'm too tired or maybe I've been staring too much at the code, but all I know is I need a solution before tomorrow morning, so it's time sensitive =P
Appreciate all the help I can get.
Thank you!
$files = array();
$filenames = array();
foreach($_FILES as $name => $fileInfo)
{
if($name != 'filename_form')
{
$files[$name] = $fileInfo;
$filenames[] = $name;
}
}
How about:
foreach ( $_FILES as $key => $value )
if ( preg_match ( '/type2/', $key ) )
// do something with the type2
else if ( preg_match ( '/type1/', $key ) )
// do something with these
<?php
foreach ($_FILES as $key => $value) {
$a = explode('_', $key);
$defaultDir = 'myDefaultDirOrPathOrWhatever';
if ($a[1] == 'type2') {
$defaultDir = 'myOtherDirWhenMultipleFiles';
$incrementPassed = $a[2];
}
if (isset($incrementPassed)) unset($incrementPassed);
}
Here is an alternate way
$keys = array_keys($thearray);
$filteredArray = array_filter($keys, 'fiterKeys');
function filterKeys($item) {
if($item != 'valuetocheck') return true;
else return false;
}
$type1_files = array(); // Array of files of first type
$type2_files = array(); // Array of files of second type
$underfined_files = array(); // Array of files of unnormal type (who knows :-P)
foreach($_FILES as $name => $value){
if($name == 'type1'){
$type1_files[] = $value;
}
elseif($name == 'type2'){
$type2_files[] = $value;
}
else{
$undefined_files[] = $value;
}
}
After foreach you'll have your files devided into arrays, which you can process later as you wish
<?php
$files = array();
foreach ($_FILES as $filename => $fileinfo) {
if (strpos($filename, 'form') !== false) {
$files[$name] = $fileinfo;
unset($_FILES[$name]);
}
}
?>
Fastest method I can think of, should be faster then any of the previous answers.
I want to use a function to recursively scan a folder, and assign the contents of each scan to an array.
It's simple enough to recurse through each successive index in the array using either next() or foreach - but how to dynamically add a layer of depth to the array (without hard coding it into the function) is giving me problems. Here's some pseudo:
function myScanner($start){
static $files = array();
$files = scandir($start);
//do some filtering here to omit unwanted types
$next = next($files);
//recurse scan
//PROBLEM: how to increment position in array to store results
//$next_position = $files[][][].... ad infinitum
//myScanner($start.DIRECTORY_SEPARATOR.$next);
}
any ideas?
Try something like this:
// $array is a pointer to your array
// $start is a directory to start the scan
function myScanner($start, &$array){
// opening $start directory handle
$handle = opendir($start);
// now we try to read the directory contents
while (false !== ($file = readdir($handle))) {
// filtering . and .. "folders"
if ($file != "." && $file != "..") {
// a variable to test if this file is a directory
$dirtest = $start . DIRECTORY_SEPARATOR . $file;
// check it
if (is_dir($dirtest)) {
// if it is the directory then run the function again
// DIRECTORY_SEPARATOR here to not mix files and directories with the same name
myScanner($dirtest, $array[$file . DIRECTORY_SEPARATOR]);
} else {
// else we just add this file to an array
$array[$file] = '';
}
}
}
// closing directory handle
closedir($handle);
}
// test it
$mytree = array();
myScanner('/var/www', $mytree);
print "<pre>";
print_r($mytree);
print "</pre>";
Try to use this function (and edit it for your demands):
function getDirTree($dir,$p=true) {
$d = dir($dir);$x=array();
while (false !== ($r = $d->read())) {
if($r!="."&&$r!=".."&&(($p==false&&is_dir($dir.$r))||$p==true)) {
$x[$r] = (is_dir($dir.$r)?array():(is_file($dir.$r)?true:false));
}
}
foreach ($x as $key => $value) {
if (is_dir($dir.$key."/")) {
$x[$key] = getDirTree($dir.$key."/",$p);
}
}
ksort($x);
return $x;
}
It returns sorted array of directories.