Convertir CSV a tabla

15

El reto

Dada una entrada CSV, genera una tabla Unicode adecuada utilizando caracteres de cuadro.

Formateo

La tabla se formateará utilizando las siguientes reglas:

  • El ancho de columna será igual al valor más largo de esa columna
  • Todos los datos de la tabla quedarán justificados
  • Cada tabla asumirá que la primera fila csv es el encabezado
  • La tabla utilizará los siguientes caracteres para sus bordes:

┌ ┬ ┐ ├ ┼ ┤ └ ┴ ┘ ─ │

Ejemplo

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

Reglas

  • Se aplican lagunas estándar
  • Puede enviar un programa completo, una función o una lambda
  • La entrada puede ser de un archivo, un argumento del programa o cualquier alternativa aceptable
  • La salida puede ser a un archivo, devuelto o cualquier alternativa aceptable
  • La entrada CSV debe tener el mismo formato que el usado en mi ejemplo.
  • La respuesta más corta en bytes gana.

La entrada CSV debe tomar la siguiente forma:

Header1,Header2,Header3 newline
Column1,Column2,Column3 newline
Column1,Column2,Column3 optional_newline
Shaun Wild
fuente
2
Creo que hay básicamente dos formas en las que puedes ir con la definición CSV. Si la parte interesante del problema es la salida, puede hacerlo tan simple como "dividir en comas" y no tener que preocuparse por cómo citar comas y cómo citar el carácter de cita. De lo contrario, podría establecer un método específico para analizar el CSV ("las comillas dobles alternan un modo en el que se ignoran las comas, dos comillas dobles seguidas producen una comilla doble literal" es bastante común, pero de ninguna manera es la única en existencia).
44
Err, problema grave: no ha especificado una condición de victoria. ¿Para qué se deben optimizar los programas? Longitud ( código-golf )?
1
Al menos los primeros tres enlaces allí definen CSV de manera diferente (y al menos dos dicen que hay muchas formas diferentes de hacerlo). Por lo tanto, supongo que "CSV" debe definirse de manera más completa para su uso en una pregunta (y que las soluciones intentarán salirse con la división en comas y no manejar el escape porque les permite ser más cortas).
2
Bien, he editado la pregunta para incluir detalles sobre el formato CSV que me gustaría que todos usen.
Shaun Wild
1
CRLF? ¿Seriamente? Eso dará una penalización bastante grande en Unix, donde CR significa algo más en los archivos de texto. Probablemente quiera reemplazar eso con "nueva línea", permitiendo que se use la nueva línea específica del sistema operativo.

Respuestas:

10

Pruebe (Dyalog) APL , 38 43 bytes

La última línea de entrada debe tener una nueva línea final.

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

Pruébalo en línea! En la versión fuera de línea de Dyalog APL, ejecute ]boxing ON -style=minpara el mismo efecto.

Explicación

{... }una función anónima donde representa el argumento:

¯1 ⌽ ⍵ rotar la nueva línea al frente

(s ←... )defina la función s de la siguiente manera y aplíquela

  1 ↓¨ suelta el primer personaje de cada

  ⊢ ⊂⍨ línea, dividida donde

  ⊃ = ⊢ el primer caracter es igual a los caracteres en la cadena

',' ,¨ luego anteponga una coma a cada línea

aplicar la función s a cada línea

{... }ahora aplique la siguiente función anónima:

  1 ↓ ⍵ suelte el primer elemento (los encabezados de fila)

  ↓ ⍉ ↑ transponer la lista de filas en una lista de columnas

  ↑¨ hacer cada elemento (una lista de entradas) en una matriz de entradas rellenadas

  ⍉ ⍪ convertir en matriz de una columna, luego transponer en matriz de una fila

  (⊃⍵) ⍪ poner el primer elemento del argumento (la lista de encabezados) en la parte superior`

Nota: Si bien los caracteres de dibujo lineal no se usan explícitamente en mi solución, son parte del conjunto de caracteres APL y también se contarían como bytes individuales.

Adán
fuente
Ver comentarios arribaIs input using list or array of strings (and no newlines) valid? Nope.
edc65
@ edc65 fijo. Gracias.
Adám
Ja, esa pantalla en caja seguramente es útil :)
Ven
2

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

Siento que esto podría mejorarse mucho, pero se me acabó el tiempo. Todas las terminaciones de línea son \nsin \r, la codificación es UTF8 sin BOM.

briantista
fuente
1

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

Sin golf:

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

Pruebas:

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

Salida:

┌──────┬───┬──────┐
│Name  │Age│Gender│
├──────┼───┼──────┤
│Shaun │19 │Male  │
│Debra │19 │Female│
│Alan  │26 │Male  │
│George│15 │Male  │
└──────┴───┴──────┘
rnso
fuente
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('└┴┘'))

Usos padEnd, que es específico de FireFox.

Mwr247
fuente
1
¿No son estos 288 bytes?
Adám el
1
@ Adám ... sí ... Solucionado
Mwr247
Usas esto mucho, ¿no es g('└┴┘')equivalente a g└┴┘(con backticks después gy al final)?
NoOneIsHere
1
padEndNo es estándar. Debe especificar el entorno de ejecución necesario.
Neil
1
Además, hay un par de lugares donde escribir `foo`+bar+`baz`: puede guardar un byte utilizando una plantilla `foo${bar}baz`.
Neil
1

JavaScript (ES6), 281 bytes

Nota: ingrese como una sola cadena con nuevas líneas, según lo solicitado por OP. Otras respuestas usan una lista de cadenas: al usar una matriz de cadenas en la entrada, puedo evitar la primera división y 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 golf

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

Prueba

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
fuente
0

Python 3, 318 bytes

-3 bytes para usar el %formato y -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)]))

Requiere entrada entre comillas.

Karl Napf
fuente
1
A mí me parecen 318 bytes.
Adám el
1
@ Adám Tienes razón, miré los caracteres.
Karl Napf el
No funciona, porque input()solo toma una línea en cada llamada. Deberá llamar input()hasta que no haya más líneas, o leer directamente desde stdin.
movatica
Además de eso: 292 bytes
movatica
0

C #, 696 bytes

Golfizado:

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

Sin golf (y más agradable, porque ^ eso no sirve de nada a nadie):

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

Pruebas:

┌──────┬───┬──────┐         ┌──────────┬───────────────────────────┬─────┐
|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
fuente
De alguna manera, sigo obteniendo 697 bytes al contar esto.
Adám
@ Adám Recién revisado, la cadena Golfed tiene 666 columnas de largo en Visual Studio. Pero ni 666 ni 697 son puntajes exactamente competitivos de todos modos :)
Pete Arden
Tiene una nueva línea final, pero incluso al eliminarla, sigue siendo 696 bytes .
Adám
@ Adám Ah ... He estado esperando una discrepancia en el recuento de letras / bytes para hacerme tropezar. Debería haber sabido con estos divertidos símbolos en este ("┼"). Actualizado, gracias :)
Pete Arden
Ver comentarios arribaIs input using list or array of strings (and no newlines) valid? Nope.
edc65
0

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

Utilizando:

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

Pruébalo en Ideone .

Denis Ibaev
fuente
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);

Descompostura

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

Pruébalo en ideone

Titus
fuente
0

APL (Dyalog Extended) , 36 25 bytes SBCS de

Programa completo Asume que ABCDEFGHIJKLMNOPQRSTUVWXYZes el archivo CSV. Imprime en stdout.

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

Pruébalo en línea!

⎕A el alfabeto en mayúscula A (la cadena incorporada más corta a referencia)
⎕CSV lee ese archivo y lo convierte de CSV a matriz de
m← almacenamiento a medida que m(para m atrix)
1↓ suelta la
 transposición de la primera fila
 dividida en la lista de columnas
↑¨ mezcla cada lista de cadenas en una matriz
(... )⍪ apile lo siguiente encima de eso:
1↑m tome la primera fila de m
⌂disp aplicar dfns.dispa eso (dibuja caracteres de dibujo lineal)

Adán
fuente