Convert a classical directory structure like this:


Into this

├── config.yml
├── drafts
|   ├── begin-with-the-crazy-ideas.textile
|   └── on-simplicity-in-technology.markdown
└── includes
    ├── footer.html
    └── header.html
  • Four spaces specify a nested folder or file of the above dir.
  • Nested categories levels allowed can vary.


  • filenames: valid Linux filenames without spaces and linefeeds: any byte except NUL, / and spaces, linefeeds
  • drawing characters:
    • | vertical line (U+007C)
    • box drawings light horizontal (U+2500)
    • box drawings light vertical and right (U+251C)

Winner: Shortest Code in Bytes wins!

  • 1
    \$\begingroup\$ Welcome to PPCG! Nice first post! \$\endgroup\$ – Rɪᴋᴇʀ Jul 8 '16 at 22:42
  • \$\begingroup\$ Are the vertical lines supposed to be \x7C Vertical Line or \u2502 Box Drawings Light Vertical? \$\endgroup\$ – Neil Jul 9 '16 at 13:47
  • \$\begingroup\$ @Neil I wasn't aware of "Box Drawings Light Vertical", I've used "Vertical Line" in the example and there are already two answers with that. It would have more sense to use the former one anyway because the other two characters are Box Drawings type, should I update the question with \u2502? \$\endgroup\$ – marcanuy Jul 9 '16 at 18:11
  • \$\begingroup\$ Since I can't easily type box drawing characters into my REPL of choice, I wrote my answer using L, + and - characters, and then adjusted the score believing you were using box drawing characters throughout, although I had in fact just copied and pasted from your question without checking. If vertical line is acceptable, I could reduce my score by 1. \$\endgroup\$ – Neil Jul 9 '16 at 18:21
  • 1
    \$\begingroup\$ Excellent, since it now saves my 2 bytes on my new answer! \$\endgroup\$ – Neil Jul 9 '16 at 18:34

Retina, 88 bytes

m`^ *
T+`|├` └`(?<=(.)*).(?!.+¶(?>(?<-1>.)*)[|├└])

Try it online!

I suppose I could technically count this as one byte per character by swapping out some characters, reading the source as ISO 8859-1 and then finding a single-byte encoding for the output which contains and , but I can't be bothered to work out the details right now. (For the record, that would be 72 bytes.)


Stage 1: Substitution

m`^ *

We start by matching the indentation on each line and inserting ├──.

Stage 2: Substitution


Next, we match every group of 4 spaces and replace the first with a |. Now all that needs fixing is | that go to the bottom of the output and that should be . Both of those cases can be recognised by looking at the character directly below the one we potentially want to change.

Stage 3: Transliteration

T+`|├` └`(?<=(.)*).(?!.+¶(?>(?<-1>.)*)[|├└])

The (?<=(.)*) counts how many characters precede the match on the current line to measure it's horizontal position. Then the lookahead skips to the next line with .+¶, matches as many characters as we've captured in group 1 with (?>(?<-1>.)*) (to advance to the same horizontal position) and then checks whether the next character (i.e. the one below the actual match) is one of |├└. If that's the case, the match fails, and in all other cases it succeeds and the stage substitutes spaces for | and for .

This won't fix all characters in a single run, so we apply this stage repeatedly with the + option until the output stops changing.

Stage 4: Substitution


All that's left is the first line, so we simply match the beginning of the string and prepend a . and a linefeed.

  • \$\begingroup\$ Explanation please? \$\endgroup\$ – Neil Jul 9 '16 at 10:41
  • \$\begingroup\$ @Neil There you go. \$\endgroup\$ – Martin Ender Jul 9 '16 at 10:59
  • \$\begingroup\$ Would it help to use +`(?<=(.*))\|(?!.+¶\1[|├]) (space) +`(?<=(.*))├(?!.+¶\1[│├└]) ? \$\endgroup\$ – Neil Jul 9 '16 at 13:56
  • \$\begingroup\$ @Neil I tried something like that but I don't think I managed to save bytes with it. \$\endgroup\$ – Martin Ender Jul 9 '16 at 15:35
  • \$\begingroup\$ New idea: m`^ * $&└── T+` └`|├`(?<=(.*)).(?=.+¶\1[|└]) \$\endgroup\$ – Neil Jul 9 '16 at 15:40

JavaScript (ES6), 237 128 bytes

f=(s,r=`.\n`+s.replace(/^ */gm,"$&└── "),q=r.replace(/^(.*)( |└)(?=.+\n\1[|└])/m,(_,i,j)=>i+`|├`[+(j>' ')]))=>q==r?r:f(s,q)

Where \n represents the literal newline character. Explanation: r is created from s by prepending the . line and inserting the └── at the end of each line's indent. This is now correct for the last line of the input, but each must be "extended" upwards as far as possible. This is the job of q, which searches for a and recursively replaces the spaces directly above it with |s unless it reaches another which gets turned into instead. The recursion ends when no further replacements can be made. Note that if the character above the is a space or a then the text to the left of the is always the same as that on the previous line so I can just use \1 to test that the one character is above the other.


Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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