The task is (supposedly) simple! Take an input (\t indented with \n newlines):


Then convert it to the following (using , , and ):

├ two
│ ├ three
│ │ └ four
│ │   └ five
│ └ six
└ seven
  └ eight
    └ nine

Turns out this is not simple and I completely underestimated it…

I did eventually figure this one out and my tool is viewable here.

Some of the main issues I faced were:

  1. When to render .
  2. Are we at the end of the list and should render?

My approach to this was to split each line out and attach some metadata.

We then loop through each line and focus on rendering the , and .

  1. Are there any lines with the same tab count?
  2. If so, does the line after with the same tab count come before any unindented lines?
const blockingLine = linesAfterThisLine.find(lineAfter => 
  lineAfter.lineNumber > line.lineNumber && 
  lineAfter.tabCount === line.tabCount

// Check if any line after is unindented & before blockingLine
if (
  linesAfterThisLine.find(lineAfter =>
    lineAfter.tabCount < blockingLine.tabCount &&
    lineAfter.lineNumber < blockingLine.lineNumber
) {
  // Render └
} else {
  // Render ├

We then loop through this output and focus on rendering the . It’s likely possible to avoid the second loop however I had spent too much time trying to “optimise” I just wanted something that worked.

  1. For each of the tab characters on the line
  2. Get the last line before itself where it’s indented to the same level as the tab depth we’re checking
  3. Depending on the content of the line, render a
for (let indentLevelToCheck = line.tabCount - 1; indentLevelToCheck > 0; indentLevelToCheck--) {
  const relatedPreviousLines = linesBeforeThisLine.filter(previousLine => previousLine.tabCount === indentLevelToCheck)
  const lastRelatedLine = relatedPreviousLines[relatedPreviousLines.length - 1]

  if (lastRelatedLine) {
    if (lastRelatedLine.content.includes("├")) {
      // Render │ 
    } else {
      lineContent = "  " + lineContent
      // Render spacing

This loop here is the first thing I’d tackle if I went at this project again since it feels wrong. However, it’s much faster than iterating over every line again for some directory structures, since it only loops based on tabCount - 1. If you knew you were only working with shallow directory depths it would be fine, but for generic use-cases it can use some work.

I recommend giving this a shot yourself in your language of choice, it’s a surprisingly difficult but fun challenge. Again, you can view my solution here!

© 2024 Pfych