Converter CSV em tabela

15

O desafio

Dada uma entrada CSV, produza uma tabela unicode adequada usando caracteres de caixa.

Formatação

A tabela será formatada usando as seguintes regras:

  • A largura da coluna será igual ao valor mais longo dessa coluna
  • Todos os dados da tabela serão justificados
  • Cada tabela assumirá a primeira linha csv como o cabeçalho
  • A tabela usará os seguintes caracteres para suas bordas:

┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ ─ │

Exemplo

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

Regras

  • Aplicam-se brechas padrão
  • Você pode enviar um programa completo, uma função ou um lambda
  • A entrada pode ser de um arquivo, um argumento do programa ou qualquer alternativa aceitável
  • A saída pode ser para um arquivo, retornado ou qualquer alternativa aceitável
  • A entrada CSV deve ter o mesmo formato usado no meu exemplo.
  • A resposta mais curta em bytes vence.

A entrada CSV deve assumir o seguinte formato:

Header1,Header2,Header3 newline
Column1,Column2,Column3 newline
Column1,Column2,Column3 optional_newline
Shaun Wild
fonte
2
Eu acho que existem basicamente duas maneiras de seguir a definição de CSV. Se a parte interessante do problema for a saída, você pode simplificá-lo como "dividir vírgulas" e não precisar se preocupar em como citar vírgulas e como citar o caractere de citação. Caso contrário, você pode declarar um método específico de analisar o CSV ("aspas duplas alternam um modo em que vírgulas são ignoradas, duas aspas duplas em uma linha produzem uma aspas duplas literais") é bastante comum, mas de maneira alguma o único existente).
4
Err, problema sério: você não especificou uma condição de vitória. Para que os programas devem ser otimizados? Comprimento ( código-golfe )?
1
Pelo menos os três primeiros links ali definem o CSV de maneira diferente (e pelo menos dois dizem que existem várias maneiras diferentes de fazer isso). Portanto, estou assumindo que "CSV" precisa ser mais completamente definido para uso em uma pergunta (e que as soluções tentarão se dividir em vírgulas e não manipular a fuga porque permite que elas sejam mais curtas).
2
Ok, editei a pergunta para incluir detalhes sobre o formato CSV que gostaria que todos usassem.
Shaun Wild
1
CRLF? Seriamente? Isso dará uma penalidade bastante grande no Unix, onde CR significa outra coisa nos arquivos de texto. Você provavelmente deseja substituir isso por "nova linha", permitindo que a nova linha específica do sistema operacional seja usada.

Respostas:

10

Tente (Dyalog) APL , 38 43 bytes

A última linha de entrada deve ter uma nova linha à direita.

{{(⊃⍵)⍪⍉⍪↑¨↓⍉↑1↓⍵}s¨',',¨(s1↓¨⊢⊂⍨⊢=⊃)¯1⌽⍵}

Experimente online! Na versão offline do Dyalog APL, execute]boxing ON -style=min para o mesmo efeito.

Explicação

{... }uma função anônima em que representa o argumento:

¯1 ⌽ ⍵ gire a nova linha à direita

(s ←... )defina a função s da seguinte maneira e aplique-a

  1 ↓¨ solte o primeiro caractere de cada

  ⊢ ⊂⍨ linha, divida onde

  ⊃ = ⊢ o primeiro caractere é igual aos caracteres da string

',' ,¨ Anexe uma vírgula a cada linha

aplique a função s a cada linha

{... }agora aplique a seguinte função anônima:

  1 ↓ ⍵ solte o primeiro elemento (os cabeçalhos da linha)

  ↓ ⍉ ↑ transpor a lista de linhas para a lista de colunas

  ↑¨ transformar cada elemento (uma lista de entradas) em uma matriz de entradas preenchidas

  ⍉ ⍪ transformar em matriz de uma coluna e transpor para matriz de uma linha

  (⊃⍵) ⍪ coloque o primeiro elemento do argumento (a lista de cabeçalhos) no topo`

Nota: Embora os caracteres de desenho de linha não sejam explicitamente usados ​​em minha solução, eles fazem parte do conjunto de caracteres APL e também são contados como bytes únicos.

Adão
fonte
Veja comentários acimaIs input using list or array of strings (and no newlines) valid? Nope.
edc65 29/11
@ edc65 Corrigido. Obrigado.
Adám 29/11
Hah, esse display em caixa certamente é útil :)
Ven
2

PowerShell 3 ou superior, 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'┴')┘"

Sinto que isso poderia melhorar muito, mas fiquei sem tempo. Todas as terminações de linha estão \nsem \r, a codificação é UTF8 sem BOM.

briantist
fonte
1

Raquete 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 '("└" "─" "┴" "┘"))
    ))

Teste:

(f (list  "Name,Age,Gender"
          "Shaun,19,Male"
          "Debra,19,Female"
          "Alan,26,Male"
          "George,15,Male"))

Resultado:

┌──────┬───┬──────┐
│Name  │Age│Gender│
├──────┼───┼──────┤
│Shaun │19 │Male  │
│Debra │19 │Female│
│Alan  │26 │Male  │
│George│15 │Male  │
└──────┴───┴──────┘
rnso
fonte
1

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('└┴┘'))

Usa padEnd, que é específico do FireFox.

Mwr247
fonte
1
Isso não é 288 bytes?
Adám 23/11/16
1
@ Adám ... sim ... Corrigido
Mwr247 23/11
Você usa muito isso, não é g('└┴┘')equivalente a g└┴┘(com backticks depois ge no final)?
NoOneIsHere
1
padEndnão é padrão. Você deve especificar o ambiente de execução necessário.
Neil
1
Além disso, existem alguns lugares onde você escreve `foo`+bar+`baz`- você pode salvar um byte usando um modelo `foo${bar}baz`.
Neil
1

JavaScript (ES6), 281 bytes

Nota: insira como uma única sequência com novas linhas - conforme solicitado pelo OP. Outras respostas usam uma lista de strings - usando uma matriz de strings na entrada, posso evitar a primeira divisão e cortar 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`
`)

Menos golfe

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`  
)

Teste

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>

edc65
fonte
0

Python 3, 318 bytes

-3 bytes para usar a %formatação e -1 para abreviarstr.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)]))

Requer entrada entre aspas.

Karl Napf
fonte
1
Parece 318 bytes para mim.
Adám 23/11/16
1
@ Adám Você está certo, eu olhei para os caracteres.
23416 Karl Napf
Não funciona, porque input()leva apenas uma linha em cada chamada. Você precisará ligar input()até que não haja mais linhas ou ler diretamente de stdin.
movatica 11/08/19
Além de que: 292 bytes
movatica
0

C #, 696 bytes

Golfe:

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 (e melhor, porque ^ que não serve para ninguém):

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;
}

Teste:

┌──────┬───┬──────┐         ┌──────────┬───────────────────────────┬─────┐
|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  |         └──────────┴───────────────────────────┴─────┘
└──────┴───┴──────┘
Pete Arden
fonte
De alguma forma, continuo recebendo 697 bytes ao contar isso.
Adám
@ Adám Apenas verificado novamente, a sequência Golfed tem 666 colunas no Visual Studio. Mas nem 666 nem 697 são partituras exatamente competitivos de qualquer maneira :)
Pete Arden
Você tem uma nova linha à direita, mas mesmo quando a remove, ela ainda tem 696 bytes .
Adám
@ Adám Ah ... Eu estava esperando uma discrepância de contagem de letras / contagem de bytes para me enganar. Deveria ter conhecido com esses símbolos engraçados neste ("┼"). Atualizado, obrigado :)
Pete Arden
Veja comentários acimaIs input using list or array of strings (and no newlines) valid? Nope.
edc65 29/11
0

Perl, 273 + 9 (-CS -nlaF, sinalizadores) = 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];{

Usando:

cat file.csv | perl -CS -nlaF, script.pl

Experimente em Ideone .

Denis Ibaev
fonte
0

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);

demolir

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

Teste em ideone

Titus
fonte
0

APL (Dyalog Extended) , 36 SBCS de 25 bytes

Programa completo. Supõe que ABCDEFGHIJKLMNOPQRSTUVWXYZé o arquivo CSV. Imprime em stdout.

disp(1m)⍪↑¨↓⍉1m←⎕CSVA

Experimente online!

⎕A a letra maiúscula Um lphabet (o mais curto-se referência incorporada na cadeia de caracteres)
⎕CSV ler esse ficheiro e converso de CSV a matriz de
m← armazenamento como m(para m Atrix)
1↓ gota a primeira fileira
 de transposição
 dividida em lista de colunas
↑¨ misturar cada lista de strings numa matriz
()⍪ Empilhe o seguinte em cima disso:
1↑m faça a primeira linha de m
⌂disp aplicar dfns.dispa isso (desenha caracteres de desenho de linha)

Adão
fonte