14
\$\begingroup\$

The Challenge

Given a CSV input, output a proper unicode table using box characters.

Formatting

The table will be formatted using the following rules:

  • Column width will be equal to the longest value of that column
  • All table data will be left justified
  • Each table will assume the first csv row to be the header
  • The table will use the following characters for its borders:

┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ ─ │

Example

Input:
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  │
└──────┴───┴──────┘

Rules

  • Standard loopholes apply
  • You may submit a full program, a function or a lambda
  • Input can be from a file, a program argument or any acceptable alternative
  • Output can be to a file, returned or any acceptable alternative
  • CSV input should take the same format as used in my example.
  • Shortest answer in bytes wins.

CSV input should take the following form:

Header1,Header2,Header3 newline
Column1,Column2,Column3 newline
Column1,Column2,Column3 optional_newline
\$\endgroup\$
  • 2
    \$\begingroup\$ I think there are basically two ways you can go with the CSV definition. If the interesting part of the problem is the output, you can make it as simple as "split on commas" and not have to worry about how to quote commas and how to quote the quote character. Otherwise, you could state a specific method of parsing the CSV ("double quotes toggle a mode in which commas are ignored, two double quotes in a row produces a literal double quote" is a fairly common one, but by no means the only one in existence). \$\endgroup\$ – user62131 Nov 21 '16 at 13:21
  • 4
    \$\begingroup\$ Err, serious problem: you haven't specified a victory condition. What are the programs meant to be optimized for? Length (code-golf)? \$\endgroup\$ – user62131 Nov 21 '16 at 13:23
  • 1
    \$\begingroup\$ At least the first three links there all define CSV differently (and at least two say that there are lots of different ways to do it). So I'm assuming that "CSV" needs to be more fully defined for use in a question (and that the solutions will try to get away with splitting on commas and not handling escaping because it allows them to be shorter). \$\endgroup\$ – user62131 Nov 21 '16 at 14:26
  • 2
    \$\begingroup\$ Okay I've edited the question to include specifics about the CSV format I would like everyone to use. \$\endgroup\$ – Shaun Wild Nov 21 '16 at 17:20
  • 1
    \$\begingroup\$ CRLF? Seriously? That's going to give a fairly large penalty on Unix, where CR means something else in text files. You probably want to just replace that with "newline", allowing the OS-specific newline to be used. \$\endgroup\$ – user62131 Nov 21 '16 at 23:29

10 Answers 10

10
\$\begingroup\$

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

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.

\$\endgroup\$
  • \$\begingroup\$ See comments above Is input using list or array of strings (and no newlines) valid? Nope. \$\endgroup\$ – edc65 Nov 29 '16 at 9:29
  • \$\begingroup\$ @edc65 Fixed. Thanks. \$\endgroup\$ – Adám Nov 29 '16 at 11:24
  • \$\begingroup\$ Hah, that boxed display surely comes in handy :) \$\endgroup\$ – Ven Nov 29 '16 at 12:00
2
\$\begingroup\$

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.

\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Looks like 318 bytes to me. \$\endgroup\$ – Adám Nov 23 '16 at 21:17
  • 1
    \$\begingroup\$ @Adám You are right, i looked at the chars. \$\endgroup\$ – Karl Napf Nov 23 '16 at 21:20
1
\$\begingroup\$

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  │
└──────┴───┴──────┘
\$\endgroup\$
1
\$\begingroup\$

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.

\$\endgroup\$
  • 1
    \$\begingroup\$ Isn't this 288 bytes? \$\endgroup\$ – Adám Nov 23 '16 at 21:18
  • 1
    \$\begingroup\$ @Adám ...yes... Fixed \$\endgroup\$ – Mwr247 Nov 23 '16 at 21:25
  • \$\begingroup\$ You use this alot, isn't g('└┴┘') equivalent to g└┴┘ (with backticks after g and at the end)? \$\endgroup\$ – NoOneIsHere Nov 23 '16 at 21:36
  • 1
    \$\begingroup\$ padEnd is non-standard. You should specify the necessary execution environment. \$\endgroup\$ – Neil Nov 24 '16 at 21:42
  • 1
    \$\begingroup\$ Also, there are a couple of places where you write `foo`+bar+`baz` - you can save a byte by using a template `foo${bar}baz`. \$\endgroup\$ – Neil Nov 24 '16 at 21:46
1
\$\begingroup\$

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>

\$\endgroup\$
0
\$\begingroup\$

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  |         └──────────┴───────────────────────────┴─────┘
└──────┴───┴──────┘
\$\endgroup\$
  • \$\begingroup\$ Somehow, I keep getting 697 bytes when counting this. \$\endgroup\$ – Adám Nov 24 '16 at 6:37
  • \$\begingroup\$ @Adám Just checked again, the Golfed string is 666 columns long in Visual Studio. But neither 666 nor 697 are exactly competitive scores anyway :) \$\endgroup\$ – Pete Arden Nov 24 '16 at 8:23
  • \$\begingroup\$ You have a trailing newline, but even when removing it, it is still 696 bytes. \$\endgroup\$ – Adám Nov 24 '16 at 8:37
  • \$\begingroup\$ @Adám Ah... I've been waiting for a letter count/byte count discrepancy to trip me up. Should have known with these funny symbols in this one ("┼"). Updated, thanks :) \$\endgroup\$ – Pete Arden Nov 24 '16 at 8:46
  • \$\begingroup\$ See comments above Is input using list or array of strings (and no newlines) valid? Nope. \$\endgroup\$ – edc65 Nov 29 '16 at 9:29
0
\$\begingroup\$

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.

\$\endgroup\$
0
\$\begingroup\$

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

Test it at ideone

\$\endgroup\$
0
\$\begingroup\$

APL (Dyalog Unicode), 36 bytesSBCS

Full program. Assumes that ABCDEFGHIJKLMNOPQRSTUVWXYZ is the CSV file. Prints to stdout.

⎕CY'dfns'
disp(1↑m)⍪↑¨⊂[1]1↓m←⎕CSV⎕A

Try it online!

⎕CY'dfns' copy everything from the dfns workspace

⎕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
⊂[1] enclose along the first axis (i.e. vertically; to process each column separately)
↑¨ 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 the disp function to that

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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