Skip to content

Instantly share code, notes, and snippets.

@tetsuo
Created July 19, 2021 20:18
Show Gist options
  • Save tetsuo/6f7a59ab58ab17a10bf8c10f9a560863 to your computer and use it in GitHub Desktop.
Save tetsuo/6f7a59ab58ab17a10bf8c10f9a560863 to your computer and use it in GitHub Desktop.
tree.ts
interface Tree<A> {
readonly value: A
readonly forest: Forest<A>
}
type Forest<A> = Array<Tree<A>>
const draw = (indentation: string, forest: Forest<string>): string => {
let r: string = ''
const len = forest.length
let tree: Tree<string>
for (let i = 0; i < len; i++) {
tree = forest[i]
const isLast = i === len - 1
r += indentation + (isLast ? '└' : '├') + '─ ' + tree.value
r += draw(indentation + (len > 1 && !isLast ? '│ ' : ' '), tree.forest)
}
return r
}
const drawForest = (forest: Forest<string>): string => draw('\n', forest)
const drawTree = (tree: Tree<string>): string => tree.value + drawForest(tree.forest)
const make = <A>(value: A, forest: Forest<A> = []): Tree<A> => ({
value,
forest,
})
interface Predicate<A> {
(a: A): boolean
}
const splitAt =
<A>(n: number) =>
(xs: Array<A>): [A[], A[]] =>
[xs.slice(0, n), xs.slice(n)]
const until =
<A>(p: Predicate<A>) =>
(f: (a: A) => A) =>
(x: A): A => {
let v = x
while (!p(v)) {
v = f(v)
}
return v
}
const succ = (x: number) => 1 + x
const span =
<A>(p: Predicate<A>) =>
(xs: Array<A>): [A[], A[]] => {
const iLast = xs.length - 1
return splitAt<A>(until<number>(i => iLast < i || !p(xs[i]))(succ)(0))(xs)
}
const forestFromLineIndents = (tuples: Array<[number, string]>) => {
const go = (xs: Array<[number, string]>): Forest<string> =>
0 < xs.length
? (() => {
const [n, s] = Array.from(xs[0]) as [number, string]
const [firstTreeLines, rest] = Array.from(span<[number, string]>(x => n < x[0])(xs.slice(1)))
return [make(s, go(firstTreeLines))].concat(go(rest))
})()
: []
return go(tuples)
}
const treeFromText = (text: string): Tree<string> =>
make('.', forestFromLineIndents(text.split('\n').map(x => [Math.ceil(x.search(/\S/) / 2), x.trimStart()])))
const text = `Edit me to generate
a
nice
tree
diagram!
:)
Use indentation
to indicate
file
and
folder
nesting.
- You can even
- use
- markdown
- bullets!`
console.log(drawTree(treeFromText(text)))
// outputs:
//
// .
// └─ Edit me to generate
// ├─ a
// │ └─ nice
// │ └─ tree
// │ ├─ diagram!
// │ └─ :)
// └─ Use indentation
// ├─ to indicate
// │ ├─ file
// │ ├─ and
// │ ├─ folder
// │ └─ nesting.
// └─ - You can even
// └─ - use
// ├─ - markdown
// └─ - bullets!
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment