# Console print a bar graph

The objective is to print a graph from an array of numbers, of the design below. Printing the X and Y scale exactly as in the "design", with padding, is part of the challenge. The input array of numbers can be of virtually any range of integers in X and Y, but not 0 and not negative - though I would suggest keeping it small considering this is ASCII art! (x < 15, y < 50 or so).

Specifics

1) the graph should print the Y scale on the left, padded to the max length of the input, and with a pipe '|' between the number and the first column.

2) the bar should have all empty spaces with periods '.' filled spaces with pipes '|' and the number itself should be an 'o'.

3) there are three "tween" columns between each number that interpolates between the two numbers. These cannot just be a duplication of the previous or next column, but need to interpolate between the values linearly. (Unless two adjacent numbers in the list are only 1 apart or the same.)

4) The bottom of the graph should look: " +---------" to the length of the graph.

5) There should be an X scale padded to the length of the max number, similar to the Y scale. The last digit of the x scale should align with the value (I guess that means we're limited to two digits...)

6) Lastly, there should be an "empty" column in the beginning of the graph and an "empty" column at the end.

Example

This:

 [5, 3, 7, 8, 10, 7, 8, 7, 9, 2]


Generates:

10|.................o.....................
09|................||...............o.....
08|.............o|||||......o......||.....
07|.........o||||||||||.o|||||..o||||.....
06|........|||||||||||||||||||||||||||....
05|.o.....||||||||||||||||||||||||||||....
04|.||...|||||||||||||||||||||||||||||....
03|.|||.o||||||||||||||||||||||||||||||...
02|.|||||||||||||||||||||||||||||||||||.o.
01|.|||||||||||||||||||||||||||||||||||||.
+---------------------------------------
01  02  03  04  05  06  07  08  09  10


Trailing spaces on each line, or trailing newlines at the end are permitted. As long as the output is visually the same.

Here's a working Python3 example, in "long hand" if you wish:

Try it online!

• I still don't understand how the graph ends up looking like this. For example, why is the next to last vertical line shorter than the last one? Or why is the one on either side of entry 2 shorter than the one for entry 2, given that entry 2 is a low point between entries 1 and 3? – Xcali Mar 29 '18 at 3:04
• I'd strongly suggest posting this in the Sandbox. Your challenge is particularly complicated and needs a lot of care to be specified exactly. Ideally the reader would not need to infer rules from an example, or to read or run any code. As this is your first challenge, I might also suggest to try writing and posting a simpler challenge first to get the hang of things, or to post some answers, then return to this. Unfortunately, a lot of community expectations of challenges aren't really written down, so a bit of experience helps a lot. – xnor Mar 29 '18 at 3:27
• Created an python program in 553 bytes, I found this an interesting challenge. Since it is on hold, I will comment it tio.run/##bVLtboMgFP3PU/… – Rick Rongen Mar 29 '18 at 8:11
• @RickRongen Although a nice answer (and you can golf 4 bytes like this), it's actually slightly different: his graph has a line after the last o, but yours does not. And rule 3 (having three "tween" heights in between) is a bit vague. Can they all 3 be the same as the o column before or after it like in this pastebin, or do they need to have a flowing line? This was one of the reasons it's closed as unclear. It's probably better to have a single distinguished height-calculation of the "tweens" instead of "logic is up to you". – Kevin Cruijssen Mar 29 '18 at 9:53
• If "the logic is up to you", which "logic" are valid? We need a objective validity criteria. Should we do linear interpolation? – user202729 Mar 29 '18 at 10:46

# Python 3, 505, 484, 480 bytes

i=list(map(int,input().split(',')))
l=len(i)
r=range
e=enumerate
m=max(*i)
s=len(str(m))
t='%%0%dd'
f=t%s
g=t%len(str(l))
d=[['.']*(l*4+s)for _ in r(m+2)]
for x,y in e(d):y[:s]=f%(m-x);y[s]='|'
d[-2][:]=' '*s+'+'+'-'*(l*4-1)
d[-1][:s+2]=' '*(s+2)
d[-1][s+1:]=['%-4s'%(g%-~x)for x in r(l)]
for x,y in e(i):
n=y
def r(z,o):
for v in d[m-n+(n-y)*z//4+1:m]:v[x*4+s+o]='|'
d[m-y][x*4+s+2]='o';r(0,2)
if x==l-1:break
n=i[x+1];r(3,3);r(2,4);r(1,5)
print('\n'.join(map(''.join,d)))


Try it online!

With some help of @kevin-cruijssen
-25 with some cleanup in newlines etc. by me

Could probably be compacted a bit more

• You can save 3 bytes with list splat in parsing input, and another 3 bytes by removing a unnecessary list clone. – user202729 Apr 4 '18 at 11:46
• You can switch == by > and do some modifications to get a -1 byte. – user202729 Apr 4 '18 at 11:52

# Python 2, 261 246 bytes

lambda a:[["   0","  +-","%02d|."%~-i][min(i,2)]+"".join(s)[3:]+" -."[min(i,2)]for i,s in enumerate(zip(*[list(("  %02d"%-~j)[i]+"-"+"o|"[i<3].rjust(a[j-1]+(v-a[j-1])*-~i/4,"|").ljust(max(a),"."))for j,v in enumerate(a)for i in range(4)]))][::-1]


Try it online!

Anonymous lambda returning the list of strings comprising the chart. Performs linear interpolation between points with floor rounding, in 4 steps from a[i-1] to a[i].

# Python 2, 271 256 bytes

lambda a:[["   0","  +-","%02d|."%~-i][min(i,2)]+"".join(s)[3:]+" -."[min(i,2)]for i,s in enumerate(zip(*[list(("  %02d"%-~j)[i]+"-"+["o".rjust(v,"|"),"|"*(a[j-1]+(v+~a[j-1])*-~i/3)][i<3].ljust(max(a),"."))for j,v in enumerate(a)for i in range(4)]))][::-1]


Try it online!

Alternative version that interpolates in the same manner as defined in the OP's reference code. Interpolation occurs in 3 steps from a[i-1] to a[i]-1. This way, the value of the third 'tween' column is always 1 below the actual data point, and these two columns always have the same number of pipes. This matches the reference code except for zero values, which we are not required to support.

Probably the longest lambda I've ever written to date. Will try to revisit and golf a bit more when I have time.

• Wow, intense use of lambda! I would have never imagined you could do this with one line of python. I do not require that the in-between columns are always 1 below the data point, the idea was to use linear interpolation. Sorry if my sample code made this unclear! – Spencer Apr 2 '18 at 19:02
• Well, that's even better for me, as it saves bytes. But I'll keep both versions for reference. – Kirill L. Apr 2 '18 at 20:28

# PHP, 231 bytes

for($e=strlen($y=max($argv));$y>$x=0;$y-=print".
")for(printf("%{$e}d|.",$y);$n=$argv[++$x];print"o.|"[$y<=>$n])while($x>1&&++$k%4)echo".|"[$y<$p+=$n/4-$p/4];echo str_pad(" +",$argc*4-1,"-"),"
";while(++$x<$argc)printf("%4d",$x);  ## fixed width (y<100), 219 bytes for($y=max($argv)+1;--$y>$x=0;print". ")for(printf("%2d|.",$y);$n=$argv[++$x];print"o.|"[$y<=>$n])while($x>1&&++$k%4)echo".|"[$y<$p+=$n/4-$p/4];echo str_pad(" +",$argc*4-1,"-"),"
";while(++$x<$argc)printf("%4d",\$x);


requires PHP 7.1 for negative string indexing. Run with -nr or try them online.

# JavaScript (Node.js), 316 bytes

a=>a.map((x,i)=>{d=Math.abs(x-~-(n=a[i+1]))/3
for(i in n?"    ":s=" ")for(j in o="|o"[+!+i].padEnd(x>n?x-d*i|0:x+d*i|0,"|")[S="padStart"](m,"."))A[j]+=o[j]},A=Array((m=Math.max(...a))+2).fill(""))&&A.map((x,i)=>x?(m-i+"|.")[S](4,0)+x+".":i>m?s+s+a.map((_,I)=>s+(-~I+s)[S](3,0)).join:"  +".padEnd(a.length*4+2,"-"))


Try it online!

# Jelly, 68 bytes

J+ȷ2DḊ€


Try it online!

# Charcoal, 110106 103 bytes

ＡθＵＢ.⮌Ｅ⌈θ⁺⁺×0⁻Ｌ⌈θＬ⊕κκ|Ｇ↓²→Ｌ⌈θ↑² ↓Ｐ⁺⪫Ｅθ◧⁺×0⁻ＬＬθＬ⊕κκ⁴ω ↗≔Ｅ⁻×⁴Ｌθ³§θ÷κ⁴θ+Ｐ⁺²Ｌθ↗↑Ｅθ⎇﹪κ⁴⁺ι÷×﹪κ⁴⁻§θ⁺⁴κι⁴⁺×|⊖ιo


Try it online! Link is to verbose version of code. Edit: Saved 3 bytes thanks to @ASCII-only. Explanation:

Ａθ


Input the array.

ＵＢ.


Set the background of the output to ..

⮌Ｅ⌈θ⁺⁺×0⁻Ｌ⌈θＬ⊕κκ|


Print the Y-axis with its labels left padded with zeros.

Ｇ↓²→Ｌ⌈θ↑² ↓


Blank out the bottom left corner so that it isn't filled with .s.

Ｐ⁺⪫Ｅθ◧⁺×0⁻ＬＬθＬ⊕κκ⁴ω ↗


Print the X labels.

≔Ｅ⁻×⁴Ｌθ³§θ÷κ⁴θ


Repeat the values in the array 4 times (except for the last value).

+Ｐ⁺²Ｌθ↗↑


Print the X-axis.

Ｅθ⎇﹪κ⁴⁺ι÷×﹪κ⁴⁻§θ⁺⁴κι⁴⁺×|⊖ιo


Print the graph itself, repeating |s with a final o for multiples of 4 and interpolating for intermediate values.