Alinee el CSV

12

Visión general:

Su trabajo es tomar la entrada CSV en key=valueformato y alinearla de una manera más organizada (ver más abajo).

Entrada:

Siempre a través de stdin . Los registros siempre estarán en la siguiente forma key=value:

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • No habrá una lista de posibles claves por adelantado, debe encontrarlas en el texto de entrada.
  • El final de la entrada se indicará mediante EOFcualquier implementación que EOFsea ​​apropiada para su sistema operativo.

Salida:

La primera fila de su salida será una lista de todas las teclas, en orden alfabético (incluso si las teclas son todas números). Después de eso, imprima cada registro en el mismo formato CSV con el encabezado de número apropiado, sin las teclas enumeradas. Entonces, para el ejemplo anterior, la salida correcta sería:

abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop

PREGUNTAS MÁS FRECUENTES:

  • ¿Tengo que preocuparme por una entrada con formato incorrecto?
    • No. Su programa puede hacer lo que quiera (lanzar una excepción, ignorar, etc.) si la entrada no está formateada correctamente, por ejemplo, una línea de foo,bar,baz
  • ¿Cómo manejo los caracteres especiales de escape?
    • Puede suponer que no habrá datos adicionales ,o =en los datos que no sean parte del key=valueformato. "no tiene ningún significado especial en este concurso (aunque sí lo tiene en el CSV tradicional). Tampoco es especial de ninguna manera.
    • Las líneas deben coincidir con la siguiente expresión regular: ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • Por lo tanto, las claves y los valores coincidirán [^=,]+
  • ¿Qué hay de CRLFfrente LF?
    • Puede elegir el delimitador que sea apropiado para su plataforma. La mayoría de los idiomas manejan esto sin un código de delimitación especial.
  • ¿Necesito imprimir comas finales si las últimas columnas no existen?
    • Si. Mira el ejemplo.
  • ¿Están permitidos los analizadores CSV u otras herramientas externas similares?
    • No. Debe analizar los datos usted mismo.
durron597
fuente
15
Preguntas frecuentes cuando nadie hizo preguntas todavía. :-)
Justin
55
@Quincunx Si me hago la pregunta que cuenta;)
durron597
18
Tengo la sensación de que así es como funcionan todas las preguntas frecuentes.
Martin Ender
¿Puedo tener una coma final en mi lista de claves y valores? Haría que mi código fuera mucho más corto ...
PlasmaPower
@PlasmaPower No entiendo la pregunta; sin embargo, su programa debe coincidir exactamente con la salida de ejemplo para la entrada de ejemplo dada
durron597

Respuestas:

3

GolfScript, 64 caracteres

n%{','/{'='/}%}%:I{{0=}/}%.&$:K','*n{`{{(2$=*}%''*\;}+K%','*n}I/

El código es una implementación sencilla en GolfScript, puede probar el ejemplo en línea .

Código anotado:

# Split the input into lines, each line into tuples [key, value]
# and assign the result to variable I
n%{','/{'='/}%}%:I

# From each tuple take the 0'th element (i.e the key)
{{0=}/}%

# Take the unique items (.&), sort ($) and assign the result to variable K
.&$:K

# Output: join values with , and append a newline
','*n

# {...}I/: Loop over all lines of the input 
{

  # `{...}+K%: Loop over all keys and initially push the current 
  # line for each of the keys
  `{
    # stack here is [current key, current line]
    # {}%: map to all the items of the current line
    {
      # extract the key from the current item and compare
      (2$=
      # if equal keep [value], otherwise multiply with 0, i.e. discard
      *
    }%
    # join the results (may be one or zero) and drop the key
    ''*\; 
  }+K%
  # Output: join values of current line with , and append a newline
  ','*n
}I/
Howard
fuente
2

Perl 6: 119 caracteres, 120 bytes

my@l=lines.map:{/[(\w+)\=(\w+)]+%\,/;push $!,~«@0;$%(@0 Z=>@1)}
say .join(",") for$!.=sort.=uniq,($(.{@$!}X//"") for@l)

De-golf:

my@l=lines.map: {
    # Parse the key=value pairs,
    # put all the keys in $/[0] (or $0)
    # put all the values in $/[1] (or $1)
    / [ (\w+) \= (\w+) ]+ % \, /;

    # Push all the keys into $!
    # (@0 just means @$0 or $/[0].list)
    push $!, ~«@0;

    # Return a hash of keys zipped into pairs with the values
    $%( @0 Z=> @1 )
}

$!.=sort.=uniq;
# …i.e., $! = $!.sort.uniq;

# Print the CSV for the keys ($!),
# followed by the CSVs for the hashes we made for each line,
# as accessed by our sorted key list. (… .{@$!} …)
# If the value doesn't exist, just use "" instead. (… X// "" …)
say .join(",") for $!, ($( .{@$!} X// "" ) for @l)
Mouq
fuente
2

perl, 129/121

129 bytes, sin interruptores de línea de comando:

for(<>){push@x,{%g=map{split/=/}split/[,
]/};@h{keys%g}=()}@k=sort keys%h;$"=",";sub P{print"@_
"}P@k;for$x(@x){P map{$$x{$_}}@k}

Como @Dennis señala a continuación, puede obtener esto a 120 + 1 = 121 utilizando -n:

push@x,{%g=map{split/=/}split/[,
]/};@h{keys%g}=()}@k=sort keys%h;$"=",";sub P{print"@_
"}P@k;for$x(@x){P map{$$x{$_}}@k

Básicamente, para cada línea, nos dividimos por comas para obtener la lista de pares. Para cada par, nos dividimos por el signo igual para obtener la clave y el valor. Establecemos el par clave / valor en% h y un hashref local. El primero se usa para determinar la lista de claves. Este último se usa para recordar los valores de esta línea.

skibrianski
fuente
1
Puede guardar algunos caracteres al: 1. Usar el -ninterruptor en lugar de for(<>){...}. 2. Dividir en [, ]lugar de usar chomp. 3. Omitiendo el punto y coma después de las llaves.
Dennis
Gracias @ Dennis. He implementado las últimas 2 de sus sugerencias. Todavía puedo agregar -n a la mezcla, pero me siento como un purista que es demasiado vago para escribir en el cajero automático de su teléfono :-) También eso requeriría un bloqueo FINAL, pero supongo que aún sería una ganancia neta .
skibrianski
Sí, agregar -n solo ahorra 3 caracteres (dos puntos) con el bloque FIN. Prefiero la solución "más pura". Al menos hasta que una de las otras respuestas se acerque =)
skibrianski
Perl literalmente envuelve while (<>) { ... }todo el script, por lo que no hay necesidad de un bloque END. Simplemente elimine for(<>){al principio y }al final del guión.
Dennis
3
Sin embargo, funcionará, siempre y cuando elimine }al final del script, no el que corresponde al forbucle. Además, puede guardar un carácter más utilizando una nueva línea real en lugar de \n.
Dennis
1

JavaScript ( ES5 ) 191 183 179 168 bytes

Suponiendo que el código se ejecuta en la línea de comando de spidermonkey:

for(b=[a={}];l=readline(i=0);b.push(c))for(c={},d=l.split(/,|=/);e=d[i++];)c[a[e]=e]=d[i++];for(;c=b[i++];)print(Object.keys(a).sort().map(function(x){return c[x]})+[])

Resultado:

> js test.js < input.txt
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop

Esta cuña se puede usar en un navegador para simular spidermonkey readliney print:

var I = 0, LINES = '\
foo=bar,baz=quux\n\
abc=123,foo=fubar\n\
baz=qwe,abc=rty,zxc=uiop,foo=asdf'.split('\n'),
readline = function(){
    return LINES[I++];
}, print = function(){
    return console.log.apply(console, arguments);
};

Sin golf:

a = {};                        // this object holds all keys found
b = [a];                       // array key:value pairs of each line, initialized with our key holder object in position 0
for(;l = readline();){         // store each line in l, loop until blank/undefined line
    c = {};                    // create a new object for this line's key:value pairs
    d = l.split(/,|=/);        // split line by commas and equals
    for(i = 0; e = d[i++];){   // loop through each key
        a[e] = e;              // set the key=key for key holder object
        c[e] = d[i++];         // set key=value for the line object
    }
    b.push(c);                 // push line object onto array
}
for(i = 0; c = b[i++];){       // loop through all line objects until undefined
    print(                     // print line
        Object.keys(a).sort(). // get sorted list of keys
        map(function(x){
            return c[x]        // map values from line object
        })
        + []                   // cast array to string
    );
}
nderscore
fuente
La pregunta no dice que tienes que usar "stdout"; puedes usarlo alerten lugar de console.logy guardar algunos bytes así.
Gaurang Tandon
@GaurangTandon Entonces, tendría que concatenar todas las líneas de contorno. Podría actualizar mi respuesta para usar la línea de comando de spidermonkey y en su lugar usar readliney printpara stdin / out real
nderscore
1

Bash + coreutils, 188 138 bytes

p=paste\ -sd,
f=`cat`
h=`tr , '\n'<<<$f|cut -d= -f1|sort -u`
$p<<<"$h"
for l in $f;{
join -o2.2 -a1 - <(tr =, ' \n'<<<$l|sort)<<<"$h"|$p
}

Salida:

$ ./lineupcsv.sh < input.csv 
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
$ 
Trauma digital
fuente
0

Haskell, 357 334

import Data.List
o=1<2
s u|u==""=""|o=tail u
t=takeWhile
d=dropWhile
c=(/=',')
e=(/='=')
p x|x/=""=Just((t e x,t c.s.d e$x),s.d c$x)|o=Nothing
g=m(unfoldr p).lines
k=nub.sort.(m fst=<<).g
[]#_=[]
(t@(x,u):z)#n@(a,b)|x==a=n:z|o=t:z#n
i=intercalate
main=interact$ \d->i"\n"$(i","$k d):m(i",".m snd.foldl(#)(m(flip(,)"").k$d))(g d)
m=map

gestá haciendo el análisis: divide la entrada en líneas y asigna cada línea a una lista de (key,value)pares. k, al concatenar todas las claves en una lista y eliminar duplicados, crea una lista con todas las claves únicas que luego puedo usar para ordenar. Hago esto creando un "Conjunto" dentro main( m(flip(,)"").k$d == [("abc",""),("baz",""),("foo",""),("zxc","")]) para cada línea, y luego tomando cada (key,value)par de una línea y colocándolo donde pertenece en la lista ( foldl). La línea 1 del ejemplo produce [("abc",""),("baz","quux"),("foo","bar"),("zxc","")], que concateno en una sola Cadena ( ",quux,bar,"), concatena con las otras líneas e imprimo.

>>> csv.exe < input.txt
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
Flonk
fuente
0

Python 2.7 - 242 bytes

bleh

import os
c=[r.split(',')for r in os.read(0,99).split('\n')]
k=sorted(list(set(sum([[s.split('=')[0]for s in r]for r in c],[]))))
print','.join(k)
for l in c:
 t=[''for i in k]
 for s in l:
    o,v=s.split('=')
    t[k.index(o)]=v
 print','.join(t)

Tenga en cuenta que la segunda capa de sangría es un solo carácter de tabulación, no cuatro espacios como SE lo representa.

Sin golf:

#!/bin/python2

import os

# I do input as a list comprehension in the original but this is equivalent
c = []

# For each line in the input
for r in os.read(0,99).split('\n'):
    # Add a list of key=value pairs in that row to c
    c.append(r.split(','))

# Another thing done as a list comprehension, but I'll space it out
k = []

# For each list of key=value pair s in c
for r in c:
    # For each actual key=value pair in that list
    for s in r:
        # Get the key
        k.append(s.split('=')[0])

# Discard dupes by converting to set and back, then sort
k = sorted(list(set(k)))

# Seperate these keys by commas, then print
print ','.join(k)

# For each line in c
for l in c:
    # t has one empty string for each key in the input
    t = ['' for i in k]
    # For each key=value pair in the line
    for s in l:
        # o = key, v = value
        o, v = s.split('=')
        # Find the position that the key is in the list of keys, then put the
        # value in t at that position
        t[k.index(o)] = v

    # Now each value is in the right position and the keys with no values on this
    # line have an empty string. Join everything with commas and print
    print ','.join(t)
metro subterráneo
fuente
0

Python 3: 200 195 192 189 187

import sys
r=[dict(p.split('=')for p in l[:-1].split(','))for l in sys.stdin]
x=set()
for d in r:x|=d.keys()
k=sorted(x)
for l in[k]+[[r.get(k,'')for k in k]for r in r]:print(*l,sep=',')
aragaer
fuente
0

k4 (40? 51? 70? 46?)

la expresión básica es

","0:{(x@<x:?,/?!:'x)#/:x}(!).'"S=,"0:/:

esto acepta y devuelve una lista de cadenas

para que coincida con la especificación, podríamos hacer interactivamente

-1","0:{(x@<x:?,/?!:'x)#/:x}(!).'"S=,"0:/:."\\cat";

que acepta la entrada de stdin e imprime la salida en stdout

para una aplicación independiente que acepte la entrada de una tubería, podríamos hacer esto:

$ cat i.k
.z.pi:{t,:`\:x}
.z.exit:{-1","0:{(x@<x:?,/?!:'x)#/:x}(!).'"S=,"0:/:t}
$ cat i.txt|q i.k
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
$ 

aunque si está dispuesto a considerar mi envoltorio k-como-filtro preexistente, awq.k, como una herramienta aceptable para este tipo de rompecabezas, entonces podemos hacer esto:

$ cat i.txt|awq.k '","0:{(x@<x:?,/?!:'\''x)#/:x}(!).'\''"S=,"0:/:'

que tiene 46 caracteres o 40, dependiendo de cómo cuente las disputas entre comillas

Aaron Davies
fuente
¿Qué tipo de entorno se necesita para ejecutar esto? q¿mando? ¿Se awq.kpublica en alguna parte?
Trauma digital
32-bit q ahora está disponible como software gratuito en kx.com/software-download.php . (solían tener solo una versión de prueba por tiempo limitado de forma gratuita.) hmm, parece que awq no está publicado en ninguna parte; Debería hacer algo al respecto.
Aaron Davies
0

C # - 369

(en LINQPAD)

void C(string a){var k=a.Split(new[]{',','\n'}).Select(s=>s.Split('=')[0]).OrderBy(o=>o).Distinct();var t=string.Join(",", k)+"\n";foreach(var x in a.Split('\n')){for(int i=0;i<k.Count();i++){foreach(var y in x.Split(',').OrderBy(o=>o.Split('=')[0]))if(k.ElementAt(i)==y.Split('=')[0])t+=y.Split('=')[1];t+=",";}t=t.Remove(t.LastIndexOf(','),1)+"\n";}Console.Write(t);}

Sin golf

void C(string a)
{
    var k=a.Split(new[]{',','\n'}).Select(s=>s.Split('=')[0]).OrderBy(o=>o).Distinct();
    var t=string.Join(",", k)+"\n";
    foreach(var x in a.Split('\n'))
    {
        for(int i=0;i<k.Count();i++)
        {
            foreach(var y in x.Split(',').OrderBy(o=>o.Split('=')[0]))
                if(k.ElementAt(i)==y.Split('=')[0])
                    t+=y.Split('=')[1];
            t+=",";
        }
        t=t.Remove(t.LastIndexOf(','),1)+"\n";
    }
    Console.Write(t);
}

Prueba de entrada de cadena

C("foo=bar,baz=quux\nabc=123,foo=fubar\nbaz=qwe,abc=rty,zxc=uiop,foo=asdf");

Salida

abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
jzm
fuente
Es curioso, esto es C #, por lo que debería funcionar en Windows, pero ¿no? (Vea mi pregunta de CRLFvs. LFPreguntas frecuentes) Desafortunadamente no tengo una copia de Visual Studio para probar.
durron597
Originalmente debería haber agregado la nota, pero ahora tengo. Si, funciona. Lo he creado y probado en linqpad. No lo he probado en la aplicación de consola, pero no hay ninguna razón por la que no funcione. Pero una aplicación de consola obviamente agregaría más bytes al código.
jzm