Text to Directory
The task is (supposedly) simple! Take an input (\t
indented with \n
newlines):
one
two
three
four
five
six
seven
eight
nine
Then convert it to the following (using ├
,
│
, and └
):
one
├ 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:
- When to render
│
. - 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.
- What line number are you on
- How many tabs are at the start of your line
- What is the content of your line
We then loop through each line and focus on rendering the
├
, and └
.
- Are there any lines with the same tab count?
- 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.
- For each of the tab characters on the line
- Get the last line before itself where it's indented to the same level as the tab depth we're checking
- 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!