12
\$\begingroup\$

Goal:

A tree can be represented as a nested list: the list elements represent nodes, and a node is a pair of the node name and node children.

Your program takes as input a nested list of pairs, and should output a pretty tree.

Examples:

Here are four example test cases below

["stump",[]]

["trunk",[["branch",[["leaf",[]],["leaf",[]]]],["branch",[]]]]

[".",[["bin",[]],["usr",[["bin",[]],["local",[["bin",[]],["lib",[]]]]]]]]

["kingdom",[["mineral",[]],["animal",[["dog",[["schnauzer",[]],["whippet",[]]]]]],["vegetable",[["cabbage",[]]]]]]

And these are the corresponding outputs

1

stump

2

trunk
├── branch
│   ├── leaf
│   └── leaf
└── branch

3

.
├── bin
└── usr
    ├── bin
    └── local
        ├── bin
        └── lib

4

kingdom
├── mineral
├── animal
│   └── dog
│       ├── schnauzer
│       └── whippet
└── vegetable
    └── cabbage

Notes:

  • If the characters , , , are not conveniently available in your language, you may substitute with the ASCII characters |, +, L, - respectively.

  • You may assume there is one unique root node. You may assume the input will be "nice", it doesn't matter what your program does if the input is not well-formed.

  • To avoid unfair advantages to any particular language, some flexibility in the input format is allowed within reason. For example, specifying the nesting with parens () or braces {} is OK. If it's more convenient for the node labels to be integers instead of strings, that's OK. Extra whitespace in the input is OK, as long as it's not being used to define structure. If you take the input as a string literal and parse it directly, that's OK.

  • No messing with the underlying structure of the tree! E.g., you may not change how a leaf node is represented.

  • Tip: the representation used in the examples is valid json, so you can paste it into a json linter to better see the nesting.

Scoring:

This is code golf, shortest code wins.

\$\endgroup\$
5
  • \$\begingroup\$ Closely related. (Same output, different input.) \$\endgroup\$ Nov 22, 2016 at 21:47
  • \$\begingroup\$ I'm not a regular on this site, if you think the question can be improved please comment or edit directly. Thanks! \$\endgroup\$
    – wim
    Nov 22, 2016 at 21:47
  • \$\begingroup\$ what is the exact input format? Is it a string or an object? \$\endgroup\$
    – Ludonope
    Nov 22, 2016 at 22:00
  • \$\begingroup\$ @Ludonope Whatever makes your code easier. See note 3. I expect most people will use an object, because writing a parser is probably not fun, but I don't want to discourage any possible interesting stack based approaches. \$\endgroup\$
    – wim
    Nov 22, 2016 at 22:07
  • 4
    \$\begingroup\$ The characters │, ├, └, ─ are conveniently available in most languages, but each one requires 3 bytes in UTF8, so using the ASCII gives a better score \$\endgroup\$
    – edc65
    Nov 24, 2016 at 16:02

6 Answers 6

4
\$\begingroup\$

Perl 5, 146 bytes

sub f{my$r=@_-1;my$p=$r?pop:[];while(my$n=shift){my($t,$c)=@$n;say$r?((map$_?'|   ':$"x4,@$p),@_?'+':L,'-- '):(),$t;f(@$c,[@$p,$r?!!@_:()])if@$c}}

Try it online!

\$\endgroup\$
3
\$\begingroup\$

Haskell, 159 bytes

With preprocessing of the input to fit the defined tree datatype (included in score) since trees can't be represented as a nested list.

data T=T[Char][T]
r=reverse;z=zipWith;a%b=a:cycle[b]
p(T s b)|k<-r$p<$>b=s:(concat$r$z(z(++))(("└── "%"    ")%("├── "%"│   "))k)
mapM putStrLn.p

Usage:

> mapM putStrLn.p $ T"kingdom"[T"mineral"[],T"animal"[T"dog"[T"schnauzer"[],T"whippet"[]]],T"vegetable"[T"cabbage"[]]]
kingdom
├── mineral
├── animal
│   └── dog
│       ├── schnauzer
│       └── whippet
└── vegetable
    └── cabbage
\$\endgroup\$
3
\$\begingroup\$

JavaScript (ES6), 123 125 bytes

Using 5 unicode graphic character, 3 bytes each. The char count is 115.

x=>(o=x[0],r=(l,p)=>l[0]&&l.map((x,i)=>r(x[1],p+'│ '[q=+!l[i+1]]+'   ',o+=`
${p+'├└'[q]}── `+x[0])),r(x[1],''),o)

Less golfed

l=>(
  o = l[0],
  r = (l,p)=> // recursive tree printer
      l[0] && 
      l.map((x,i)=>
             (
               q = +!l[i+1],
               o += `\n${p+'├└'[q]}── ` + x[0],
               r(x[1], p+'│ '[q]+'   ')
             )
      ), 
  r(l[1],''),
  o
)

F=
x=>(o=x[0],r=(l,p)=>l[0]&&l.map((x,i)=>r(x[1],p+'│ '[q=+!l[i+1]]+'   ',o+=`
${p+'├└'[q]}── `+x[0])),r(x[1],''),o)

out=x=>O.textContent+=x+'\n\n'

;[["stump",[]]
,["trunk",[["branch",[["leaf",[]],["leaf",[]]]],["branch",[]]]]
,[".",[["bin",[]],["usr",[["bin",[]],["local",[["bin",[]],["lib",[]]]]]]]]
,["kingdom",[["mineral",[]],["animal",[["dog",[["schnauzer",[]],["whippet",[]]]]]],["vegetable",[["cabbage",[]]]]]]
].forEach(t=>out(JSON.stringify(t)+'\n'+F(t)))
<pre id=O></pre>

\$\endgroup\$
3
\$\begingroup\$

Python 3, 110 109 bytes

Down to 109 thanks to att

def f(l,p='',a=0,o=0):
 x,c=l;print(p+(('+L'[o]+'-- ')*a)+x)
 for i in c:f(i,p+('| '[o]+'   ')*a,1,c[-1]is i)

Call as:

f(["kingdom",[["mineral",[]],["animal",[["dog",[["schnauzer",[]],["whippet",[]]]]]],["vegetable",[["cabbage",[]]]]]])

To get output:

kingdom
+-- mineral
+-- animal
|   L-- dog
|       +-- schnauzer
|       L-- whippet
L-- vegetable
    L-- cabbage

This is my first code-golf, so I welcome any feedback or suggestions. I ended up using flags for the recursive function to know when an element was the root node or when it was the last leaf of a node, allowing it to differentiate the prefix that needed to be applied. I wanted to have the last argument for the recursive call originally be i==c[-1], but ran into an issue with the call

f(["trunk",[["branch",[["leaf",[]],["leaf",[]]]],["branch",[]]]])

where the two branches are equivalent but not identical, so a is call was necessary instead of ==.

\$\endgroup\$
1
  • \$\begingroup\$ c[-1]is i for -1 \$\endgroup\$
    – att
    Sep 9, 2021 at 21:45
3
\$\begingroup\$

K (ngn/k), 96 bytes

` 0:4_'{r:o@x,y[1],"   ";(,x,(*y),"-- ",*z),/$[z@1;("+|"r/:-1_z@1),"L "r/:-1#z@1;""]}["";"  ";]@

Try it online!

Quite a shame that this took so long, but I'm glad that I stuck with the problem.

Recursive function which prints each line of the tree separately. ngn/k's json parser is used to get the array.

-6 bytes from coltim.

-1 from ngn.

\$\endgroup\$
1
  • \$\begingroup\$ I was able to trim 6 bytes with some incremental golfs. \$\endgroup\$
    – coltim
    Sep 9, 2021 at 15:16
1
\$\begingroup\$

Python 2, 118 119 bytes

def f(x,q='',p='',r=''):
 m,w=x;l=len(w);print q+r+m
 for i in range(l):a=l-i>1;f(w[i],q+p,' |'[a]+'   ','L+'[a]+'-- ')

Call as:

f(["kingdom",[["mineral",[]],["animal",[["dog",[["schnauzer",[]],["whippet",[]]]]]],["vegetable",[["cabbage",[]]]]]])

Outputs:

kingdom
+-- mineral
+-- animal
|   L-- dog
|       +-- schnauzer
|       L-- whippet
L-- vegetable
    L-- cabbage
\$\endgroup\$
0

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

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