What I have is a list of file paths:

dir0/file0.txt
dir0/dir1/filea.txt
dir0/file1.txt
dir0/dir1/fileb.txt
dir0/dir1/dir1/filec.txt
dir0/file2.txt

What I'd like to get is something like the following:

$paths=Array(
	file0.txt=>file0.txt,
	dir1=>Array(
		filea.txt=>filea.txt,
		fileb.txt=>fileb.txt,
		dir1=>Array(
			filec.txt=>filec.txt
		)
	),
	file1.txt=>file1.txt,
	file2.txt=>file2.txt
);

It strikes me that this must be a fairly common requirement so it must've been solved plenty of times.

However, I'm not finding it easy to get my head round it.

You mean something like this?

Hi n_e,

Thanks for responding.

Sorry, just seen the borked url....easily fixed. I'll look at it, thanks.

...It's not quite what I'm after.

I already have a list of paths.

What I'm looking to do is sort them into a tree hiearchy.

I didn't get any help from anyone here, but I think I've managed to solve it.

At least the code seems to work.

<style>

.menu {
	list-style: url("openIcon.png");}
.menu_none {
	list-style: none;}
.submenu{
	display: none;
	list-style: none;
        margin-left: 20px;
}

</style>

<script language="JavaScript" type="text/JavaScript">

function toggle(id) {
    var toggle_block = document.getElementById(id);
    if(toggle_block.style.display == "block"){
        toggle_block.style.display = "none";}else{toggle_block.style.display = "block";
    }
}


</script>


<?php
    $paths=Array();
    // Put some paths into the array for testing
    $paths[]='dir0/file0.txt';
    $paths[]='dir0/dir1/filea.txt';
    $paths[]='dir0/file1.txt';
    $paths[]='dir0/dir1/fileb.txt';
    $paths[]='dir0/dir1/dir1a/filec.txt';
    $paths[]='dir0/file2.txt';
    $paths[]='dir0/dir2/dir1d/filec.txt';
    $paths[]='dir3/dir1b/dir1c/filec.txt';
    // These need to be sorted.
    sort($paths);
    print ('<ul class="menu">');
    $directories=array ();
    $topmark=0;
    $submenu=0;
    foreach ($paths as $value) {
        // break up each path into it's constituent directories
        $limb=explode("/",$value);
        for($i=0;$i<count($limb);$i++) {
            if ($i+1==count($limb)){// It's the 'Leaf' of the tree, so it's a file
                if ($topmark>$i){// the previous path had more directories, therefore more Unordered Lists.
                    print str_repeat("</ul>",$topmark-$i);// Close off the Unordered Lists
                    print ("\n");// For neatness
                }
                print ('<li><a href="">'.$limb[$i]."</a></li>\n");// Print the Leaf (file)
                $topmark=$i;// Establish the number of directories in this path
            }else {// It's a directory
                if($directories[$i]!=$limb[$i]){// If the directory is the same as the previous path we are not interested.
                    if ($topmark>$i){// the previous path had more directories, therefore more Unordered Lists.
                        print str_repeat("</ul>",$topmark-$i);// Close off the Unordered Lists
                        print ("\n");// For neatness
                    }
                    // Print the Directory as a concertina dropdown menu.
                    print ("<li onclick=\"toggle('submenu$submenu');\">".$limb[$i]."</li>\n<ul id=\"submenu$submenu\" class=\"submenu\">\n");
                    $submenu++;// Increment the dropdown.
                    $directories[$i]=$limb[$i];// Mark it so that if the next path's directory in a similar position is the same, it won't be processed.
                }
            }
        }
    }
    print str_repeat("</ul>",$topmark+1);// Close off the Unordered Lists
?>
Member Avatar for diafol

Could you say why you need this? There are loads of new shiney file/dir handling functions in php5.

The RecursiveIteratorIterator function and its mates are great (SPL functions).

http://www.php.net/manual/en/book.spl.php

Sure.

Tell me now, after I've already spent 2 weeks trying to solve the problem.

:?

>>Tell me now,

He just did.

I don't get it.

Two weeks ago I posted a request for help on something I am now informed has plenty of solutions to. (Please don't tell me that "Googleunderstand is your friend"; I googled plenty, although obviously using inappropriate searchphrases)

So I spend 2 weeks cracking a problem that has really been difficult to get my head round.

When I think I have it solved I come back here to post my solution so that others who are having the same problem can benefit.

And find I'm ridiculed for my pains.

Member Avatar for diafol

>And find I'm ridiculed for my pains.

Que?

You've posted a solution - something that forum browsers can use. Well done. Can't see the 'ridiculed' bit myself. I was just pointing you at Iterators (SPL) which can significantly reduce the amount of code you need to employ. Take it or leave it - no skin off my nose.

I feel your pain Dave. I understand how frustrating searching for a solution to a problem can be.

I am really sorry to hear you spent two weeks trying to solve this problem.

I may have a better solution for you. I ran into this problem today and started looking. After finding this realized I just just give it a shot. I came up with this...

<?php
$paths=Array();
$paths[]='dir0/file0.txt';
$paths[]='dir0/dir1/filea.txt';
$paths[]='dir0/file1.txt';
$paths[]='dir0/dir1/fileb.txt';
$paths[]='dir0/dir1/dir1a/filec.txt';
$paths[]='dir0/file2.txt';
$paths[]='dir0/dir2/dir1d/filec.txt';
$paths[]='dir3/dir1b/dir1c/filec.txt';

/*Temporary Array used for sorting*/
$depths = array();

/*Explode all file paths to create multi-dimensional arrays*/
foreach( $paths as $path )
{
	$depths[] = explode( "/", $path );
}
/*Sort your multi-dimensional array*/
sort( $depths );

/*Implode the sorted files back to their orignal states*/
foreach( $depths as $path )
{
	echo implode( "/", $path ) . "<br/>";
}
?>

I didn't get any help from anyone here, but I think I've managed to solve it.

At least the code seems to work.

<style>

.menu {
	list-style: url("openIcon.png");}
.menu_none {
	list-style: none;}
.submenu{
	display: none;
	list-style: none;
        margin-left: 20px;
}

</style>

<script language="JavaScript" type="text/JavaScript">

function toggle(id) {
    var toggle_block = document.getElementById(id);
    if(toggle_block.style.display == "block"){
        toggle_block.style.display = "none";}else{toggle_block.style.display = "block";
    }
}


</script>


<?php
    $paths=Array();
    // Put some paths into the array for testing
    $paths[]='dir0/file0.txt';
    $paths[]='dir0/dir1/filea.txt';
    $paths[]='dir0/file1.txt';
    $paths[]='dir0/dir1/fileb.txt';
    $paths[]='dir0/dir1/dir1a/filec.txt';
    $paths[]='dir0/file2.txt';
    $paths[]='dir0/dir2/dir1d/filec.txt';
    $paths[]='dir3/dir1b/dir1c/filec.txt';
    // These need to be sorted.
    sort($paths);
    print ('<ul class="menu">');
    $directories=array ();
    $topmark=0;
    $submenu=0;
    foreach ($paths as $value) {
        // break up each path into it's constituent directories
        $limb=explode("/",$value);
        for($i=0;$i<count($limb);$i++) {
            if ($i+1==count($limb)){// It's the 'Leaf' of the tree, so it's a file
                if ($topmark>$i){// the previous path had more directories, therefore more Unordered Lists.
                    print str_repeat("</ul>",$topmark-$i);// Close off the Unordered Lists
                    print ("\n");// For neatness
                }
                print ('<li><a href="">'.$limb[$i]."</a></li>\n");// Print the Leaf (file)
                $topmark=$i;// Establish the number of directories in this path
            }else {// It's a directory
                if($directories[$i]!=$limb[$i]){// If the directory is the same as the previous path we are not interested.
                    if ($topmark>$i){// the previous path had more directories, therefore more Unordered Lists.
                        print str_repeat("</ul>",$topmark-$i);// Close off the Unordered Lists
                        print ("\n");// For neatness
                    }
                    // Print the Directory as a concertina dropdown menu.
                    print ("<li onclick=\"toggle('submenu$submenu');\">".$limb[$i]."</li>\n<ul id=\"submenu$submenu\" class=\"submenu\">\n");
                    $submenu++;// Increment the dropdown.
                    $directories[$i]=$limb[$i];// Mark it so that if the next path's directory in a similar position is the same, it won't be processed.
                }
            }
        }
    }
    print str_repeat("</ul>",$topmark+1);// Close off the Unordered Lists
?>

I was needing something like this for a c# project, so i converted the code and it worked a treat, many thanks davecoventry for the time you invested on this!

Hey Dave, Sorry this is kind of late. I feel your pain with the harsh comments after the fact. Anyway I was trying to figure this out all day yesterday. I found your post but I wanted an multidimensional array and not just output the results. After sleeping on it I came up with a little bit of a hacked solution by building the array as a string, then using eval, lol. Anyway it works. I had a huge file with a list of paths called sections. You can probably modify this script to work with other things.
Also I had a weird problem with trim (to get the \n off the end of my path), it was killing numeric keys and changing them to 0. For some reason replacing with a space and then removing it did the trick.

<?php
$filename = 'sections';

$data = array();
foreach(file($filename) as $section) {
	$chunks = explode('/', $section);
	$arrstr = "\$data2 = array('Base'";
	foreach($chunks as $chunk) {
		if(empty($chunk)) continue;
		$chunk = str_replace("\n", ' ', $chunk);
		$chunk = substr($chunk, 0, -1);
		$arrstr .= " => array('".$chunk."'";
	}
	$arrstr .= " => array()".str_repeat(')', count($chunks) + 1).';';
	eval($arrstr);
	$data = array_merge_recursive($data, $data2);
}

print_r($data);
?>
Member Avatar for diafol

> I feel your pain with the harsh comments after the fact.

What harsh comments?

Be a part of the DaniWeb community

We're a friendly, industry-focused community of developers, IT pros, digital marketers, and technology enthusiasts meeting, networking, learning, and sharing knowledge.