| Bytes | Lang | Time | Link |
|---|---|---|---|
| 088 | Uiua | 250408T112910Z | noodle p |
| 524 | AWK | 241205T182706Z | xrs |
| 609 | Scala 3 | 240515T091830Z | 138 Aspe |
| 259 | JavaScript Node.js | 220426T015448Z | noodle p |
| 588 | C gcc | 220616T160512Z | evanstar |
| 261 | JavaScript | 220421T033803Z | Matthew |
| 238 | PowerShell for Windows | 220423T074627Z | mazzy |
| 025 | APL Dyalog Extended | 180619T191555Z | Adá |
| 043 | Try Dyalog APL | 161121T141328Z | Adá |
| 281 | JavaScript ES6 | 161129T074956Z | edc65 |
| 365 | PowerShell 3+ | 161121T215807Z | briantis |
| 313 | PHP | 161128T163152Z | Titus |
| 286 | JavaScript ES6|FireFox | 161123T204513Z | Mwr247 |
| 578 | Racket | 161125T041450Z | rnso |
| 282 | Perl | 161124T172954Z | Denis Ib |
| 696 | C# | 161124T012158Z | Pete Ard |
| 318 | Python 3 | 161121T135938Z | Karl Nap |
Uiua, 89 88 bytes
⍜⊣˜⊂⍜⊢⊟≡˜⊂+4_8_4⟜≡⊂-32⟜≡/◇(⊂⊂)"┬┼┴"¤⍚↯⊙@─≡◇⧻⍚⊢⟜(°⊂≡⊂≡⊂⟜⤚/◇≡(⊂⊂)@│)⍚⬚@ ≡°□⍉°csv
Try it: Uiua pad
(The 88 bytes is when encoded with a byte for everything in ASCII + Uiua's glyphs, and an extra byte added to the UTF-16 representation for everything else. It's possible to reduce the character count but it would increase the byte count.)
AWK, 524 bytes
func L(A,C,D,E,F,G){f(A)f(H="─",C)f(D)f(H,E)f(D)f(H,F)f(G RS)}func f(x,y){for(y=y?y:1;y--;)printf x}{x++;for(i=0;i++<NF;)if(l[i]<(t=length($i)))l[i]=t+1;for(i=0;i++<NF;)a[x][i]=$i}BEGIN{FS=","}END{V="│"
L("┌",l[1],"┬",l[2],l[3],"┐")f(V)f(a[1][1])f(" ",l[1]-length(a[1][1]))f(V)f(a[1][2])f(" ",l[2]-length(a[1][2]))f(V)f(a[1][3])f(" ",l[3]-length(a[1][3]))f(V RS)L("├",l[1],"┼",l[2],l[3],"┤")
for(j=1;j++<x;print"|")for(k=0;k++<NF;)f(V a[j][k])f(" ",l[k]-length(a[j][k]))
L("└",l[1],"┴",l[2],l[3],"┘")}
It's heavy but works. I might come back to it in the future with fresh eyes.
Scala 3, 609 bytes
609 bytes, it can be golfed more.
Golfed version. Attempt This Online!
T=>{
val t=T.split("\n").map(_.split(","))
val h=t(0)
val d=t.tail
val w=h.indices.map(i=>t.map(_(i).length).max).toArray
def l(x:Array[String],i:Option[Int])={
val b=i.isEmpty
val(a,b1,c,p)=if(b){
x(0)match {
case "┌"=>("┌","┬","┐",'─')
case "├"=>("├","┼","┤",'─')
case "└"=>("└","┴","┘",'─')
}
}else{
("│","│","│",' ')
}
a+w.zipWithIndex.map{case(n,j)=>
(if(p==' ')x(j)else"").padTo(n,p)
}.mkString(b1)+c+"\n"
}
val tB=l(Array("┌"),None)
val hL=l(h,Some(0))
val mB=l(Array("├"),None)
val dL=d.map(r=>l(r,Some(1))).mkString
val bB=l(Array("└"),None)
tB+hL+mB+dL+bB
}
Ungolfed version. Attempt This Online!
object Main extends App {
def formatTable(tableCSV: String): String = {
val table = tableCSV.split("\n").map(_.split(","))
val header = table.head
val data = table.tail
val widths = header.indices.map(i => table.map(row => row(i).length).max).toArray
def drawLine(x: Array[String], i: Option[Int]): String = {
val isBorder = i.isEmpty
val (a, b, c, pad) = if (isBorder) {
x.head match {
case "┌" => ("┌", "┬", "┐", '─')
case "├" => ("├", "┼", "┤", '─')
case "└" => ("└", "┴", "┘", '─')
}
} else {
("│", "│", "│", ' ')
}
a + widths.zipWithIndex.map { case (n, j) =>
(if (pad == ' ') x(j) else "").padTo(n, pad)
}.mkString(b) + c + "\n"
}
val topBorder = drawLine(Array("┌"), None)
val headerLine = drawLine(header, Some(0))
val middleBorder = drawLine(Array("├"), None)
val dataLines = data.map(row => drawLine(row, Some(1))).mkString
val bottomBorder = drawLine(Array("└"), None)
topBorder + headerLine + middleBorder + dataLines + bottomBorder
}
val tableCSV = """
Name,Age,Gender
Shaun,19,Male
Debra,19,Female
Alan,26,Male
George,15,Male
""".trim
println(formatTable(tableCSV))
}
JavaScript (Node.js), 291 259 bytes
(s,[h,...r]=s.split`
`.map(l=>l.split`,`.map((z,i)=>(b[i]=Math.max(b[i]|0,z.length),z)),b=[]).map(l=>(t="│")+l.map((z,i)=>z.padEnd(b[i])).join(t)+t),d=c=>b.map(n=>"─".repeat(n)).join(c))=>`┌${d`┬`}┐
${h}
├${d`┼`}┤
${r.join`
`}
└${d`┴`}┘`
2 bytes shorter than Jensen’s :)
C (gcc), 588 bytes
#define H char
P(c,n){for(H*s[]={"\n","┌","┬","┐","├","┼","┤","└","┴","┘","─","│"};n--;)printf("%s",s[c]);}S(s,n)H*s;{printf("%-*s",n,s);}E(n,c,Z,i)int*Z;{for(i=P(3*n+1,1);i<c;P(3*n+(++i-c?2:3),1))P(10,Z[i]);P(0,1);}V(t,c,Z,r,i)H**t;int*Z;{for(i=P(11,1);i<c;i++,P(11,1))S(t[i+r],Z[i]);P(0,1);}f(s,p)H*s,*p;{int r,R,C,i,m,L;R=C=i=m=r=0;for(p=s;*p;*p++==44&&C++)!(p[1]&&*p^10)&&R++;C/=R;H*t[R*++C];for(p=s;i<R*C;*p++=0)for(t[i++]=p;*p^44&&*p^10;)p++;int Z[C];for(i=0;i<C;Z[i++]=m)for(m=r=0;r<R;m=m<L?L:m)L=strlen(t[i+C*r++]);E(0,C,Z);V(t,C,Z,0);for(E(i=1,C,Z);i<R;)V(t,C,Z,C*i++);E(2,C,Z);}
JavaScript, 261 bytes
(t,[H,...D]=T=t.split`
`.map(r=>r.split`,`),w=H.map((_,i)=>Math.max(...T.map(r=>r[i].length))),L=(x,i,_,[[[a,b=a,c=a]],p]=1/i?`│`:[x,'─'])=>a+w.map((n,j)=>(p?'':x[j]).padEnd(n,p)).join(b)+c+`
`)=>L`┌┬┐`+L(H,0)+L`├┼┤`+D.map(L).join``+L`└┴┘`
Ungolfed and explained
(
tableCSV,
[header, ...data] = table = tableCSV.split(`\n`).map(row => row.split(`,`)),
widths = header.map((_, i) => Math.max(...table.map(row => row[i].length))),
drawLine = ( // create data row or border
x, // either data array or singleton string array
i, // either data index or undefined
_, // ignored (data.map(drawLine) sets this to data)
[[[a, b = a, c = a]], pad] = 1 / i ? `│` : [x, '─']
// a = start, b = separator, c = end, pad = '─' if making border
) =>
a +
widths.map((n, j) => (pad ? '' : x[j]).padEnd(n, pad)).join(b) +
c +
`\n`
) =>
drawLine`┌┬┐` +
drawLine(header, 0) +
drawLine`├┼┤` +
data.map(drawLine).join(``) +
drawLine`└┴┘`
PowerShell for Windows, 256...236 238 bytes
$v=$args-split'
'-ne''|%{,($_-split',')}
function z($b,$a){$b[1]+(($v[0]|%{@($a)[$x++]+''|% p*ht($v|%{$_[$x-1]}|% le*|sort -b 1)$b[0]})-join$b[2])+$b[3]}
z '─┌┬┐'
$v|%{z ' │││'$_;if(!$r++){z '─├┼┤'}}
z '─└┴┘'
Notes:
- The
Sortalias defined for Windows only. It needSort-Objectfor Linux insteadsortor commandset-alias sort sort-objectbefore this script (TIO way). - The
p*htshortcut expands to thePadRight-method for a string. - The
le*shortcut expands to theLength-method for a string.
An input csv contains:
- comma and newline are delimiters only (no comma and no newline inside values|headers)
- spaces in the values|headers are significant letter (keep due justifying)
- at least 2 not empty lines (headers and values)
- an "optional_newline" at the end
- optional empty values|headers
- optional empty inner lines
APL (Dyalog Extended), 36 25 bytesSBCS
Full program. Assumes that ABCDEFGHIJKLMNOPQRSTUVWXYZ is the CSV file. Prints to stdout.
⌂disp(1↑m)⍪↑¨↓⍉1↓m←⎕CSV⎕A
⎕A the uppercase Alphabet (the shortest-to-reference built-in string)
⎕CSV read that file and convert from CSV to matrix
m← store as m (for matrix)
1↓ drop the first row
⍉ transpose
↓ split into list of columns
↑¨ mix each list of strings into a matrix
(…)⍪ stack the following on top of that:
1↑m take the first row of m
⌂disp apply dfns.disp to that
(draws line drawing characters)
Try (Dyalog) APL, 38 43 bytes
Last input line must have a trailing newline.
{{(⊃⍵)⍪⍉⍪↑¨↓⍉↑1↓⍵}s¨',',¨(s←1↓¨⊢⊂⍨⊢=⊃)¯1⌽⍵}
Try it online! In the offline version of Dyalog APL, execute ]boxing ON -style=min for the same effect.
Explanation
{ ... } an anonymous function where ⍵ represents the argument:
¯1 ⌽ ⍵ rotate the trailing newline to the front
(s ← ... ) define the function s as follows, and apply it
1 ↓¨ drop the first character of each
⊢ ⊂⍨ line, split where
⊃ = ⊢ the first character equals the characters in the string
',' ,¨ then prepend a comma to each line
s¨ apply the function s to each line
{ ... } now apply the following anonymous function:
1 ↓ ⍵ drop the first element (the row headers)
↓ ⍉ ↑ transpose the list of rows into list of columns
↑¨ make each element (a list of entries) into a matrix of padded entries
⍉ ⍪ make into one-column matrix, then transpose into one-row matrix
(⊃⍵) ⍪ put the argument's first element (the list of headers) on top`
Note: While the line drawing characters are not explicitly used in my solution, they are part of the APL character set, and would also be counted as single bytes.
JavaScript (ES6), 281 bytes
Note: input as a single string with newlines - as requested by OP. Other answers use a string list - using a string array in input I can avoid the first split and cut 9 bytes.
l=>(l=l.split`
`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[k=0]),l=l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),[h=c.map(x=>'─'.repeat(x)),l.shift(),h,...l,h].map(a=>'│┌├└'[j=a!=h?0:++k]+a.join('│┬┼┴'[j])+'│┐┤┘'[j]).join`
`)
Less golfed
l=>(
// split input in an array of string arrays
// meanwhile find the column widths and put them in *c*
l = l.split`\n`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[]),
// pad each column to the max column width
l = l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),
// put in *h* the horizontal lines for top,bottom and head separator
h = c.map(x => '─'.repeat(x) ),
// add the *h* line at top, bottom and after head line
l = [h, l.shift(), h, ...l, h],
// rebuild a string, joining columns with '|' unless the row is *h*
// if the row is *h* use different characters to join columns
k = 0,
l.map(a=> '│┌├└'[j=a!=h?0:++k] + a.join('│┬┼┴'[j]) + '│┐┤┘'[j])
.join`\n`
)
Test
F=
l=>(l=l.split`
`.map(r=>r.split`,`.map((w,i)=>(v=w.length)<c[i]?w:(c[i]=v,w)),c=[k=0]),l=l.map(r=>r.map((v,i)=>(v+' '.repeat(c[i]-v.length)))),[h=c.map(x=>'─'.repeat(x)),l.shift(),h,...l,h].map(a=>'│┌├└'[j=a!=h?0:++k]+a.join('│┬┼┴'[j])+'│┐┤┘'[j]).join`
`)
function update() {
O.textContent = F(I.value)
}
update()
#I { width:60%; height: 8em}
<textarea id=I>Name,Age,Gender
Shaun,19,Male
Debra,19,Female
Alan,26,Male
George,15,Male</textarea><br>
<button onclick='update()'>Go</button>
<pre id=O></pre>
PowerShell 3+, 365 bytes
$d=$input|ipcsv
$h=$d[0].PSObject.Properties.Name|%{$_|Add-Member -type NoteProperty -na c -v(($d.$_+$_|measure Length -ma).Maximum)-pa}
"┌$(($h|%{'─'*$_.c})-join'┬')┐"
"│$(($h|%{$_.PadRight($_.c)})-join'│')│"
"├$(($h|%{'─'*$_.c})-join'┼')┤"
$d|%{$i=$_;"│$(($h|%{$i.$_.PadRight($_.c)})-join'│')│"}
"└$(($h|%{'─'*$_.c})-join'┴')┘"
I feel like this could be improved a lot but I ran out of time. All line endings are \n with no \r, encoding is UTF8 with no BOM.
PHP, 313 bytes
for(;$r=fgetcsv(STDIN);$a[]=$r)foreach($r as$x=>$s)$e[$x]=max($e[$x],strlen($s));$t=["┬","┌","┐"];eval($L='foreach($e as$i=>$n)echo$t[!$i],str_repeat("─",$n);echo"$t[2]\n";');foreach($a as$k=>$r){foreach($r as$i=>$s)echo"│",str_pad($s,$e[$i]);echo"│\n";$t=["┼","├","┤"];if(!$k)eval($L);}$t=["┴","└","┘"];eval($L);
breakdown
for(;$r=fgetcsv(STDIN);$a[]=$r) // read csv from STDIN, append to array $a
foreach($r as$x=>$s)$e[$x]=max($e[$x],strlen($s)); // remember max length in array $e
// print top border
$t=["┬","┌","┐"];eval($L='foreach($e as$i=>$n)echo$t[!$i],str_repeat("─",$n);echo"$t[2]\n";');
foreach($a as$k=>$r)
{
foreach($r as$i=>$s)echo"│",str_pad($s,$e[$i]);echo"│\n"; // print row
$t=["┼","├","┤"];if(!$k)eval($L); // print border below header
}
$t=["┴","└","┘"];eval($L); // print bottom border
JavaScript (ES6|FireFox), 286 bytes
f=>(d=f.split`
`.map(a=>a.split`,`),s=d[0].map((a,i)=>d.reduce((b,c)=>(n=c[i].length)>b?n:b,0)),d=d.map(a=>`│${a.map((b,i)=>b.padEnd(s[i])).join`│`}│`),d.splice(1,0,(g=h=>h[0]+s.map(a=>'─'.repeat(a)).join(h[1])+h[2])('├┼┤')),g('┌┬┐')+`
${d.join`
`}
`+g('└┴┘'))
Uses padEnd, which is FireFox specific.
Racket 578 bytes
(let*((ll(map(λ(x)(string-split x","))ll))(lr list-ref)(sl string-length)(d display)(dl displayln)(nc(length(lr ll 0)))
(nl(for/list((i nc))(apply max(for/list((j ll))(sl(lr j i))))))(pl(λ(sy)(d(lr sy 0))(for((n nc))(for((m(lr nl n)))(d(lr sy 1)))
(if(< n(sub1 nc))(d(lr sy 2))(dl(lr sy 3))))))(g(λ(i n)(for((m(-(lr nl n)(sl i))))(d" ")))))(pl'("┌""─""┬""┐"))
(for((i(lr ll 0))(n(in-naturals)))(d"│")(d i)(g i n))(dl"│")(pl'("├""─""┼""┤"))(for((j(range 1(length ll))))
(for((i(lr ll j))(n nc))(d"│")(d i)(g i n))(dl"│"))(pl'("└" "─" "┴" "┘")))
Ungolfed:
(define(f1 ll)
(let* ((ll (map (λ (x)(string-split x ",")) ll)) ; use this to convert csv format to list of lists;
(lr list-ref) ; make short names of standard fns
(sl string-length)
(d display)
(dl displayln)
(nc (length (lr ll 0))) ; number of cols;
(nl(for/list ((i nc)) ; get list of max string-length for each column
(apply max
(for/list ((j ll))
(sl (lr j i))
))))
(pl (λ (sy) ; put lines using sent symbol list
(d (lr sy 0))
(for ((n nc))
(for ((m (lr nl n))) (d (lr sy 1)))
(if (< n (sub1 nc))
(d (lr sy 2))
(dl (lr sy 3))
))))
(g (λ (i n) ; pad with spaces if needed
(for ((m (- (lr nl n) (sl i)))) (d " ")) )))
; put line above header:
(pl '("┌" "─" "┬" "┐"))
; put header:
(for ((i (lr ll 0)) (n (in-naturals)))
(d "│")
(d i)
(g i n)
)
(dl "│")
; put line below header;
(pl '("├" "─" "┼" "┤"))
; put rows:
(for ((j (range 1 (length ll))))
(for ((i (lr ll j))
(n nc))
(d "│")
(d i)
(g i n)
)
(dl "│")
)
; put bottom line:
(pl '("└" "─" "┴" "┘"))
))
Testing:
(f (list "Name,Age,Gender"
"Shaun,19,Male"
"Debra,19,Female"
"Alan,26,Male"
"George,15,Male"))
Output:
┌──────┬───┬──────┐
│Name │Age│Gender│
├──────┼───┼──────┤
│Shaun │19 │Male │
│Debra │19 │Female│
│Alan │26 │Male │
│George│15 │Male │
└──────┴───┴──────┘
Perl, 273 + 9 (-CS -nlaF, flags) = 282 bytes
$v[$.-1]=[@F];map$l[$_]<($l=length$F[$_])&&($l[$_]=$l),0..$#F}sub p{printf$p,@_}sub o{p
pop,map{$\x$l[$_],$_-$#l?$_[0]:pop}0..$#l}$p=join'%s','',(map"\%-${_}s",@l),$/;($\,$c,@c)=map
chr$_*4+9472,0,.5,3..15;o@c[8,1,0];p($c,map{$_,$c}@$_),$i++||o@c[12,6,4]for@v;o@c[10,3,2];{
Using:
cat file.csv | perl -CS -nlaF, script.pl
Try it on Ideone.
C#, 696 Bytes
Golfed:
string T(string[]f){int w=f.Max(r=>r.Length),a=f.Select(r=>r.Split(',')[0].Length).Max(),b=f.Select(r=>r.Split(',')[1].Length).Max(),c=f.Select(r=>r.Split(',')[2].Length).Max();string o="",n="\r\n",d="",j=string.Concat(Enumerable.Repeat("─",a)),k=string.Concat(Enumerable.Repeat("─",b)),l=string.Concat(Enumerable.Repeat("─",c));Func<string,int,string>z=(q,p)=>{return q.PadRight(p);};d="┌"+j+"┬"+k+"┬"+l+"┐";o+=d+n;var g=f.First().Split(',');o+="|"+z(g[0],a)+"|"+z(g[1],b)+"|"+z(g[2],c)+"|";d="├"+j+"┼"+k+"┼"+l+"┤";o+=n+d+n;for(int i=1;i<f.Length;i++){var h=f[i].Split(',');o+="|"+z(h[0],a)+"|"+z(h[1],b)+"|"+z(h[2],c)+"|"+n;}d="└"+j+"┴"+k+"┴"+l+"┘";o+=d;return o;}
Ungolfed (and nicer, because ^that is no use to anyone):
public string T(string[] c)
{
int width = c.Max(r => r.Length),
longestFirstColumn = c.Select(r => r.Split(',')[0].Length).Max(),
longestSecondColumn = c.Select(r => r.Split(',')[1].Length).Max(),
longestThirdColumn = c.Select(r => r.Split(',')[2].Length).Max();
string o = "", lr = "\r\n", border = "",
firstColumnFiller = string.Concat(Enumerable.Repeat("─", longestFirstColumn)),
secondColumnFiller = string.Concat(Enumerable.Repeat("─", longestSecondColumn)),
thirdColumnFiller = string.Concat(Enumerable.Repeat("─", longestThirdColumn));
Func<string, int, string> padRight = (a, b) => { return a.PadRight(b); };
border = "┌" + firstColumnFiller
+ "┬" +
secondColumnFiller + "┬"
+ thirdColumnFiller
+ "┐";
o += border + lr;
var firstRow = c.First().Split(',');
o += "|" + padRight(firstRow[0], longestFirstColumn) +
"|" + padRight(firstRow[1], longestSecondColumn) +
"|" + padRight(firstRow[2], longestThirdColumn) + "|";
border = "├" +
firstColumnFiller + "┼" +
secondColumnFiller + "┼" +
thirdColumnFiller
+ "┤";
o += lr + border + lr;
for (int i = 1; i < c.Length; i++)
{
var row = c[i].Split(',');
o += "|" + padRight(row[0], longestFirstColumn) + "|"
+ padRight(row[1], longestSecondColumn) + "|" +
padRight(row[2], longestThirdColumn) + "|" + lr;
}
border = "└" +
firstColumnFiller + "┴" +
secondColumnFiller + "┴" +
thirdColumnFiller
+ "┘";
o += border;
return o;
}
Testing:
┌──────┬───┬──────┐ ┌──────────┬───────────────────────────┬─────┐
|Name |Age|Gender| |Name |PPCG Challenge |Votes|
├──────┼───┼──────┤ ├──────────┼───────────────────────────┼─────┤
|Shaun |19 |Male | |Pete Arden| Print all integers | 4 |
|Debra |19 |Female| |Pete Arden| Yes of course I'm an adult| 3 |
|Alan |26 |Male | |Pete Arden| 5 Favorite Letters | 1 |
|George|15 |Male | └──────────┴───────────────────────────┴─────┘
└──────┴───┴──────┘
Python 3, 318 bytes
-3 bytes for using the % formatting and -1 for abbreviating str.join
L=[c.split(',')for c in input().split('\n')]
m=[max(len(x)for x in c)for c in zip(*L)]
L=[[""]+[d.ljust(n)for d,n in zip(c,m)]+[""]for c in L]
g=["─"*i for i in m]
J=str.join
print('\n'.join(["┌%s┐"%J("┬",g),J("│",L[0]),"├%s┤"%J("┼",g)]+[J("│",L[i])for i in range(1,len(L))]+["└%s┘"%J("┴",g)]))
Requires input enclosed in quotes.