17
$\begingroup$

How do I plot the content of a directory and all its subdirectories into infinity using TreeForm? I've tried using a mix of FileNames, StringSplit and GatherBy but in the end I couldn't find even a promising start.

$\endgroup$
5
  • $\begingroup$ Does it have to be TreeForm? Could it be TreePlot? $\endgroup$
    – Joel Klein
    Commented Jan 7, 2013 at 3:37
  • $\begingroup$ Yes, that would work too (I choose TreeForm because I thought formatting the list in that way would not be too hard). $\endgroup$
    – C. E.
    Commented Jan 7, 2013 at 3:53
  • 1
    $\begingroup$ Take a look at the makeTree function in Leonid's answer on implementing Tries. By redefining the RHS of the first line in the definition as makeTree[StringSplit[wrds, "/"]], you can get a good structure for the directory tree with makeTree@FileNames["*", {"*"}, ∞] (you'll have to clean up the {{} -> {}}). While this can't be plugged in a built-in graph function like Graph or TreePlot, it can be easily converted to a graph using some custom code. $\endgroup$
    – rm -rf
    Commented Jan 7, 2013 at 4:48
  • 6
    $\begingroup$ An alternatieve presentation here: mathematica.stackexchange.com/a/6200/57 $\endgroup$ Commented Jan 7, 2013 at 6:27
  • $\begingroup$ Thx @Hypnotoad your solution worked well. Posted it below. (Thx to everyone else as well!) $\endgroup$
    – C. E.
    Commented Jan 7, 2013 at 20:36

3 Answers 3

10
$\begingroup$

Here's a relatively straightforward "first version":

ellipsizeMax = 8;
ellipsize[str_] := If[StringLength[str] > ellipsizeMax, StringTake[str, ellipsizeMax] <> "\[Ellipsis]", str];

readDir[currentDirectory_, 0] := ellipsize[FileNameTake[currentDirectory]];
readDir[currentDirectory_, level_] := Module[{joinedFiles, perFile},
   SetDirectory[currentDirectory];
   joinedFiles = FileNameJoin[{currentDirectory, #}] & /@ FileNames[];

   perFile[file_] := If[DirectoryQ[file],
     FileNameTake[file] @@ readDir[file, level - 1],
     ellipsize[FileNameTake[file]]];

   perFile /@ joinedFiles
   ];

treeDir[dir_] := TreeForm[dir @@ readDir[dir, 5]];

treeDir["C:/blah/blah"]

This has a very poor display capacity if there are more than a certain number of files in a directory level.

It would need a lot of work to make the presentation reasonable in the general case. But this form of tree presentation of a file system also has a lot of potential. For example, you could build a graphical navigation system based on this (though not necessarily with TreeForm).

$\endgroup$
2
  • $\begingroup$ I posted an answer shamefully stealing your code. Hope you don't mind $\endgroup$ Commented Jan 7, 2013 at 11:57
  • 1
    $\begingroup$ @belisarius Absolutely not. Plus, a lot of my answers (such as this one) are more rough sketches than complete answers. I'm more interested in the "conversation," so to speak. :) $\endgroup$
    – amr
    Commented Jan 7, 2013 at 20:19
12
$\begingroup$

The following is a slight modification to @amr's code.

It shows a directory tree using TreeForm[], with a button at each vertex. When the button is pressed, it opens a dialog with the list of the files contained in that directory.

ellipsizeMax = 8;
ellipsize[str_] := 
  If[StringLength[str] > ellipsizeMax, 
   StringTake[str, ellipsizeMax] <> "\[Ellipsis]", str];

readDir[currentDirectory_, 0] := ellipsize[FileNameTake[currentDirectory]];
readDir[currentDirectory_, level_] := 
  Module[{joinedFiles, perFile}, SetDirectory[currentDirectory];
   joinedFiles = FileNameJoin[{currentDirectory, #}] & /@ FileNames[];
   perFile[file_] := 
    If[DirectoryQ[file], file @@ readDir[file, level - 1], 
     Sequence @@ {}]; perFile /@ joinedFiles];


treeDir[dir_] := Module[{k}, 
                TreeForm[dir @@ readDir[dir, 5],
                     VertexRenderingFunction -> 
                         (Inset[Button[#2,
                                If[(k = FileNames["*.*", #2]) == {},
                                        CreateDialog["No Files", WindowTitle -> #2],
                                        CreateDialog[k, WindowTitle -> #2]]], #1] &)]]

treeDir["C:\\ff2"]

Mathematica graphics

$\endgroup$
3
$\begingroup$

Here is the code required to implement Hypnotoad's solution (see comment to the question). I've copied Leonid's code over from its original thread.

The one problem I had with this solution was that FileNames["*", {"*"}, ∞] doesn't include empty directories. I replaced it with FileNames["*", Directory[], ∞], which solves that. But Directory[] is an absolute path so all those folders leading up to your current folder will be shown in the graph. I don't know how to solve that, one would have to find a replacement to {"*"} which includes directories but at the same time produces relative paths.

ClearAll[makeTree];
makeTree[wrds : {__String}] := makeTree[StringSplit[wrds, "/"]];
makeTree[wrds_ /; MemberQ[wrds, {}]] := 
  Prepend[makeTree[DeleteCases[wrds, {}]], {} -> {}];
makeTree[wrds_] := 
 Reap[If[# =!= {}, Sow[Rest[#], First@#]] & /@ 
    wrds, _, #1 -> makeTree[#2] &][[2]]

ClearAll[getSubTree];
getSubTree[word_String, tree_] := 
 Fold[#2 /. #1 &, tree, Characters[word]]

ClearAll[inTreeQ];
inTreeQ[word_String, tree_] := 
 MemberQ[getSubTree[word, tree], {} -> {}]

ClearAll[getWords];
getWords[start_String, tree_] := 
  Module[{wordStack = {}, charStack = {}, words}, 
   words[{} -> {}] := wordStack = {wordStack, StringJoin[charStack]};
   words[sl_ -> ll_List] := Module[{}, charStack = {charStack, sl};
     words /@ ll;
     charStack = First@charStack;];
   words[First@
     Fold[{#2 -> #1} &, getSubTree[start, tree], 
      Reverse@Characters[start]]];
   ClearAll[words];
   Flatten@wordStack];
SetDirectory["~/dirtest"];

TreeForm[
 DeleteCases[
   makeTree@Select[
     FileNames["*", Directory[], \[Infinity]], DirectoryQ
     ], {} -> {}, {0, Infinity}] //. {Rule[a_, {}] :> a, 
   Rule[b_, c_] :> Apply[b, c]},
 VertexRenderingFunction -> (Style[Text[#2, #1], 14, 
     Background -> White] &)
 ]
$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.