Code Golf: Directory Tree -> Tree

11

Contest (!): In the language of your choice, write a program that will traverse the directory tree of a given directory and output a tree (i.e., an array of arrays) corresponding to it. Assume the directory is a predefined variable D. Smallest character count wins.

Rules:

  • You must use recursion
  • See Rules

Note: Assume that there are no recursion depth limits. In other words, your code just needs to work for sufficiently small directory trees, and in principle for larger ones.

For instance:

Directory tree is

dir1
├── dir11
│   ├── file111
│   └── file112
├── dir12
│   ├── file121
│   ├── file122
│   └── file123
├── file11
├── file12
└── file13

Output tree is

[[[],[]],[[],[],[]],[],[],[]]

First code golf here so lemme know if I'm doing something wrong.

Have fun :)

Andrew Odesky

Posted 2014-01-08T07:05:35.853

Reputation: 213

Windows Batch/CMD, 4 bytes: tree – cat – 2015-12-19T13:36:39.313

7"Rules: 1. You must use recursion 2. See rules" Ah!! HELP I'M STUCK IN AN INFINITE LOOP! – Justin – 2014-01-08T07:42:07.100

1You could go by the character count, or you could go by smallest size in bytes (this way programs with unicode characters are larger than if they used pure ascii) – Justin – 2014-01-08T07:43:21.213

1How deep would it traverse? – It'sNotALie. – 2014-01-08T07:47:03.890

Many people would appreciate it if you instead gave an input of a file (as a path, or something else) and they could simply output it. Also, your output seems a bit hard to understand. Can you provide a test case? Rather than using an array of arrays, could we simply print each directory/file on its own line, but indented to show subfolderness? Basically, must we output in a certain format (in which case, give an example), or can we choose a format (as long as it is unambiguous)? – Justin – 2014-01-08T07:48:19.850

@Quincunx: Edited for clarity – Andrew Odesky – 2014-01-08T07:58:47.743

@It'sNotALie: See added note – Andrew Odesky – 2014-01-08T08:01:18.507

3I'm going blind, parsing your output format. This, from someone who enjoys Lisp. – Darren Stone – 2014-01-08T08:41:59.790

can there be multiple level 0 directories?? – Vogel612 – 2014-01-08T11:07:04.420

@Vogel612: No, there is one specified level 0 directory D to be parsed – Andrew Odesky – 2014-01-08T11:17:47.857

Answers

6

Mathematica 120 21 20

enter image description here

Explicit recursion (thanks alephalpha for saving one char):

f=f/@__~FileNames~#&

f["~/StackExchange/dir1"]

{{{}, {}}, {{}, {}, {}}, {}, {}, {}}

TreeForm[%]

enter image description here

Previous overcomplicated solution:

d="~/StackExchange/dir1"

f@{x___,Longest@s:{y_,___}..,z___}:=f@{x,f@Drop[{s},1,1],z}
f[FileNameSplit/@FileNames[__,SetDirectory@d;"",∞]]/.f->(#&)

ybeltukov

Posted 2014-01-08T07:05:35.853

Reputation: 1 841

f=f/@__~FileNames~#& – alephalpha – 2014-01-13T06:04:25.123

2

Ruby, 38 chars

If you don't mind some extra whitespace in the output:

f=->n{Dir[n+'/*'].map{|c|f[c]}}
p f[D]

Example usage:

D='C:/work/dir1'
f=->n{Dir[n+'/*'].map{|c|f[c]}}
p f[D]

Output:

[[[], []], [[], [], []], [], [], []]

If I can't have the whitespace, something like this for the second line:

puts"#{f[D]}".tr' ',''

Paul Prestidge

Posted 2014-01-08T07:05:35.853

Reputation: 2 390

2

Python 2.7, 111 chars

Takes the target path from stdin.

import os
def R(d):return[R(f)for f in[d+'/'+e for e in os.listdir(d)]if os.path.isdir(f)]
print R(raw_input())

Bob

Posted 2014-01-08T07:05:35.853

Reputation: 844

2

Powershell - 182 Char

function A([string]$b){write-host -NoNewline '['; ls -path $b|foreach{if($_.PSIsContainer){A($_.FullName)}ELSE{write-host -NoNewline $f'[]';$f=', '}};write-host -NoNewline ']'};A($D)

Fairly simple. Could be reduced by 10 characters if the commas weren't required. Takes input from $D (as stated in question), returns output on STD-Out as the example in the question went.

Really wishing aliases could use options! I'm being killed by the 'write-host -NoNewline's!

lochok

Posted 2014-01-08T07:05:35.853

Reputation: 3 139

I don't know if you've actually hit the target that the challenge is aiming for... but that's not a big deal since everyone who responded seems to have chosen their own interpretation. – H.R.Rambler – 2015-01-23T17:53:47.963

{doh! Hit enter by accident. } That being said I'm not going to touch your foreach{} interpretation, I'm just going to point to an improvement you can make. The first powershell trick you're missing is that write-host is unnecessary, if you end your code with data in the pipeline it's written to the host. The second trick is the automagic expand and concatenate that happens within double-quotes. Finally use get-alias to identify tricks like % = foreach. Next time use a strategy that encases your results within a variable then ends by calling that variable: $a = gi $d | ls | %{}; "[$a]" – H.R.Rambler – 2015-01-23T18:08:07.077

I think it might be able to be done a little better. An more experienced golfers want to give it a crack? – lochok – 2014-01-16T11:25:44.310

1

C# 200 chars

Outputting a string, not an actual array. Takes a path as the first argument.

using D=System.IO.DirectoryInfo;class P{static string R(D d){var r="[";foreach(D e in d.GetDirectories())r+=R(e);return r+"]";}static void Main(string[] a) {System.Console.WriteLine(R(new D(a[0])));}}

Ungolfed:

using D = System.IO.DirectoryInfo;

class P
{
    static string R(D d)
    {
        var r = "[";
        foreach (D e in d.GetDirectories())
            r += R(e);
        return r + "]";
    }

    static void Main(string[] a)
    {
        System.Console.WriteLine(R(new D(a[0])));
    }
}

Bob

Posted 2014-01-08T07:05:35.853

Reputation: 844

My first golf attempt, and C# is a rather verbose language. Any advice would be appreciated. – Bob – 2014-01-13T03:56:05.830

0

C++, 318 bytes

#include <cstdio>
#include <dirent.h>
#include <string>
#define s std::string
#define n e->d_name
s l(s p){s r;dirent*e;DIR*d;if(d=opendir(p.c_str())){int c=0;while(e=readdir(d))if(s("..")!=n&s(".")!=n)r+=&",["[!c++]+(e->d_type==DT_DIR?l(p+'/'+n):"")+"]";closedir(d);}return r;}main(){puts((s("[")+l(D)+"]").c_str());}

Here is a slightly ungolfed version:

#include <cstdio>
#include <dirent.h>
#include <string>

#define s std::string
#define n e->d_name

s l(s p) {
    s r;
    dirent*e;
    DIR*d;
    if (d=opendir(p.c_str())) {
        int c=0;
        while (e=readdir(d))
            if (s("..")!=n&s(".")!=n)
                r+=&",["[!c++]+(e->d_type==DT_DIR?l(p+'/'+n):"")+"]";
        closedir(d);
    }
    return r;
}

main() {
    puts((s("[")+l(D)+"]").c_str());
}

Please notice that since -- per instructions -- D is assumed to be a predefined variable, the code does not build without somehow supplying D. Here is one way to build:

g++ -Dmain="s D=\".\";main" -o tree golfed.cpp

treamur

Posted 2014-01-08T07:05:35.853

Reputation: 581

0

Batch script - 146, 157, 152 127 bytes

set x=
:a
set x=%x%,[
cd %1
goto %errorlevel%
:0
for /f %%a in ('dir/b') do call:a %%a
cd..
:1
set x=%x:[,=[%]
cls
@echo %x:~1%

Run with:

scriptfile.cmd folderroot

Robert Sørlie

Posted 2014-01-08T07:05:35.853

Reputation: 1 036

The output gets bigger on every run of this script. – unclemeat – 2014-01-13T04:16:14.997

1Yeah, it wasn't very session friendly, but should be better now – Robert Sørlie – 2014-01-13T07:21:54.553