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$ 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$ 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$ 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$ 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$ 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$

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.