Alinhe o CSV


12

Visão geral:

Seu trabalho é pegar a entrada CSV no key=valueformato e alinhá-la de uma maneira mais organizada (veja abaixo).

Entrada:

Sempre via stdin . Os registros sempre estarão no seguinte formato key=value:

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • Não haverá lista de chaves possíveis com antecedência, você deve encontrá-las no texto de entrada.
  • O fim da entrada será sinalizado por EOFqualquer implementação EOFapropriada para o seu sistema operacional.

Resultado:

A primeira linha da sua saída será uma lista de todas as chaves, em ordem alfabética (mesmo que as chaves sejam todos números). Depois disso, imprima cada registro no mesmo formato CSV no cabeçalho do número apropriado, sem as teclas listadas. Portanto, para o exemplo acima, a saída correta seria:

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

PERGUNTAS FREQUENTES:

  • Preciso me preocupar com entradas formatadas incorretamente?
    • Não. O seu programa pode fazer o que quiser (lançar uma exceção, ignorar etc.) se a entrada não estiver formatada corretamente, por exemplo, uma linha de foo,bar,baz
  • Como lidar com caracteres especiais de escape?
    • Você pode assumir que não haverá dados adicionais ,ou =nos dados que não fazem parte do key=valueformato. "não tem nenhum significado especial neste concurso (mesmo que tenha no CSV tradicional). também não é especial de forma alguma.
    • As linhas devem corresponder à seguinte regex: ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • Portanto, chaves e valores corresponderão [^=,]+
  • E quanto a CRLFvs. LF?
    • Você pode escolher qualquer delimitador apropriado para sua plataforma. A maioria dos idiomas lida com isso sem código delimitante especial.
  • Preciso imprimir vírgulas à direita se as últimas colunas não existirem?
    • Sim. Veja o exemplo.
  • Analisadores CSV ou outras ferramentas externas semelhantes são permitidos?
    • Não. Você deve analisar os dados você mesmo.

15
FAQ quando ninguém fez perguntas ainda. :-)
Justin

5
@Quincunx Se eu me fazer a pergunta que importa;)
durron597

18
Sinto que é assim que todas as perguntas frequentes funcionam.
Martin Ender

Posso ter uma vírgula à direita na minha lista de chaves e valores? Não faria o meu código muito mais curto ...
PlasmaPower

@PlasmaPower Não entendi a pergunta; no entanto, o seu programa deve corresponder exatamente o exemplo de saída para o exemplo dado de entrada
durron597

Respostas:


3

GolfScript, 64 caracteres

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

O código é uma implementação direta no GolfScript, você pode testar o exemplo online .

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/

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-golfe:

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)

2

perl, 129/121

129 bytes, sem opções de linha 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 aponta abaixo, você pode obter isso para 120 + 1 = 121 usando -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

Basicamente, para cada linha, dividimos por vírgulas para obter a lista de pares. Para cada par, dividimos pelo sinal de igual para obter a chave e o valor. Definimos o par chave / valor em% h e um hashref local. O primeiro é usado para determinar a lista de chaves. O último é usado para lembrar os valores para esta linha.


1
Você pode salvar alguns caracteres: 1. Usando a -nopção em vez de for(<>){...}. 2. Dividir ao [, ]invés de usar chomp. 3. Omitindo o ponto-e-vírgula após os colchetes.
Dennis

Obrigado @Dennis. Eu implementei as duas últimas sugestões. Eu ainda posso jogar na mistura, mas estou me sentindo como um purista que é muito preguiçoso para digitar no telefone dele :-) Também isso exigiria um bloco END, mas suponho que ainda seria uma vitória líquida .
usar o seguinte código

Sim, adicionar -n salva apenas 3 caracteres (dois pontos) com o bloco END. Eu prefiro a solução "mais pura". Pelo menos até que uma das outras respostas se aproxima =)
skibrianski

Perl literalmente envolve while (<>) { ... }todo o script, então não há necessidade de um bloco END. Basta remover for(<>){no início e }no final do script.
Dennis

3
Funcionará, no entanto, desde que você remova }o final do script, não o correspondente ao forloop. Além disso, você pode salvar mais um caractere usando uma nova linha real em vez de \n.
Dennis

1

JavaScript ( ES5 ) 191 183 179 168 bytes

Supondo que o código seja executado na linha de comando 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

Esse calço pode ser usado em um navegador para simular o spidermonkey readlinee 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);
};

Ungolfed:

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

A questão não diz que você precisa usar "stdout" - você pode usar alertno lugar de console.loge salvar alguns bytes assim.
Gaurang Tandon

@GaurangTandon Então, eu teria que concatenar todas as linhas de contorno. Posso atualizar a minha resposta de usar linha de comando spidermonkey e passar a usar readlinee printpara stdin real / out
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
}

Resultado:

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

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á fazendo a análise - divide a entrada em linhas e mapeia cada linha para uma lista de (key,value)pares. k, concatenando todas as chaves em uma lista e removendo duplicatas, cria uma lista com todas as chaves exclusivas que posso usar posteriormente para classificar. Eu faço isso criando um "Set" dentro main( m(flip(,)"").k$d == [("abc",""),("baz",""),("foo",""),("zxc","")]) para cada linha e, em seguida, pegando cada (key,value)par de uma linha e colocando-o onde ele pertence na lista ( foldl). A linha 1 do exemplo produz [("abc",""),("baz","quux"),("foo","bar"),("zxc","")], concatenada em uma única String ( ",quux,bar,"), concatenada com as outras linhas e impressa.

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

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)

Observe que a segunda camada de recuo é um caractere de tabulação único, e não quatro espaços como SE o renderiza.

Ungolfed:

#!/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)

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=',')

0

k4 (40? 51? 70? 46?)

a expressão básica é

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

isso aceita e retorna uma lista de strings

para corresponder às especificações, poderíamos fazer interativamente

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

que aceita a entrada de stdin e imprime a saída em stdout

para um aplicativo independente que aceita entrada de um canal, poderíamos fazer o seguinte:

$ 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
$ 

embora você queira considerar meu invólucro k-as-filter preexistente, awq.k, como uma ferramenta aceitável para esse tipo de quebra-cabeça, podemos fazer o seguinte:

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

que tem 46 caracteres ou 40, dependendo de como você conta a disputa entre aspas


Que tipo de ambiente é necessário para executar isso? qcomando? É awq.kpublicado em algum lugar?
Digital Trauma

O q de 32 bits está agora disponível como freeware em kx.com/software-download.php . (eles costumavam ter apenas uma versão de avaliação por tempo limitado de graça.) hmm, parece que o awq não é realmente publicado em lugar algum; eu deveria fazer algo sobre isso.
Aaron Davies

0

C # - 369

(em 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);}

Ungolfed

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

Entrada da cadeia de teste

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

Resultado

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

Apenas curioso, este é o C #, por isso deve funcionar no Windows, mas funciona? (Veja minha pergunta CRLFvs. LFperguntas frequentes) Infelizmente, não tenho uma cópia do Visual Studio para testar.
precisa saber é o seguinte

Originalmente, eu deveria ter adicionado a nota, mas tenho agora. Sim, funciona. Eu criei e testei no linqpad. Não testei no aplicativo de console, mas não há motivo para que não funcione. Mas um aplicativo de console obviamente adicionaria mais bytes ao código.
JZM
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.