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.

Recommended Answers

All 12 Replies

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.