Wyrównaj CSV

12

Przegląd:

Twoim zadaniem jest pobranie formatu CSV key=valuei ułożenie go w bardziej uporządkowany sposób (patrz poniżej).

Wejście:

Zawsze za pośrednictwem standardowego wejścia . Zapisy będą zawsze miały następującą formę key=value:

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • Nie będzie z góry listy możliwych kluczy, musisz je znaleźć w tekście wejściowym.
  • Koniec wejścia zostanie zasygnalizowany przez EOFdowolną implementację EOFodpowiednią dla twojego systemu operacyjnego.

Wynik:

Pierwszy wiersz wyniku będzie listą wszystkich klawiszy, w kolejności alfabetycznej (nawet jeśli wszystkie klawisze są cyframi). Następnie wydrukuj każdy rekord w tym samym formacie CSV z odpowiednim nagłówkiem numeru, bez wymienionych kluczy. Tak więc w powyższym przykładzie poprawne dane wyjściowe to:

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

FAQ:

  • Czy muszę się martwić o niewłaściwie sformatowane dane wejściowe?
    • Nie. Twój program może zrobić, co chce (wyrzucić wyjątek, zignorować itp.), Jeśli dane wejściowe nie są poprawnie sformatowane, np. Wiersz foo,bar,baz
  • Jak radzić sobie z uciekającymi postaciami specjalnymi?
    • Możesz założyć, że nie będzie żadnych dodatkowych danych ,ani =danych, które nie są częścią key=valueformatu. "nie ma żadnego specjalnego znaczenia w tym konkursie (chociaż ma to miejsce w tradycyjnym CSV). nie jest też w żaden sposób wyjątkowy.
    • Linie powinny pasować do następującego wyrażenia regularnego: ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • Dlatego oba klucze i wartości będą do siebie pasować [^=,]+
  • Co o CRLFwersetach LF?
    • Możesz wybrać dowolny separator odpowiedni dla Twojej platformy. Większość języków obsługuje to bez specjalnego kodu ograniczającego.
  • Czy muszę drukować przecinki końcowe, jeśli kilka ostatnich kolumn nie istnieje?
    • Tak. Zobacz przykład.
  • Czy parsery CSV lub inne podobne narzędzia zewnętrzne są dozwolone?
    • Nie. Musisz samodzielnie przeanalizować dane.
durron597
źródło
15
FAQ, gdy nikt jeszcze nie zadał pytań. :-)
Justin
5
@Quincunx Jeśli zadam sobie pytanie, które się liczy;)
durron597
18
Mam wrażenie, że tak działają wszystkie najczęściej zadawane pytania.
Martin Ender
Czy na mojej liście kluczy i wartości mogę mieć przecinek końcowy?
Sprawiłoby
@PlasmaPower Nie rozumiem pytania; jednak twój program musi dokładnie pasować do przykładowego wyjścia dla podanego przykładowego wejścia
durron597

Odpowiedzi:

3

GolfScript, 64 znaki

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

Kod jest prostą implementacją w GolfScript, możesz przetestować przykład online .

Kod z adnotacjami:

# 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
źródło
2

Perl 6: 119 znaków, 120 bajtów

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

Gra w golfa:

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
źródło
2

perl, 129/121

129 bajtów, brak przełączników wiersza poleceń:

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}

Jak wskazuje @Dennis poniżej, możesz uzyskać to do 120 + 1 = 121, używając -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

Zasadniczo dla każdej linii dzielimy przecinki, aby uzyskać listę par. Dla każdej pary dzielimy według znaku równości, aby uzyskać klucz i wartość. Ustawiamy parę klucz / wartość w% h i lokalny skrót. Pierwszy służy do ustalenia listy kluczy. Ten ostatni służy do zapamiętywania wartości dla tego wiersza.

skibrianski
źródło
1
Możesz zapisać kilka znaków: 1. Używając -nprzełącznika zamiast for(<>){...}. 2. Dzielenie się [, ]zamiast używania chomp. 3. Pominięcie średnika po nawiasach klamrowych.
Dennis
Dzięki @Dennis. Wdrożyłem 2 ostatnie z twoich sugestii. Mogę jeszcze włączyć -n do miksu, ale czuję się jak purysta, który jest zbyt leniwy, aby pisać na swoim bankomacie w telefonie :-) Także to wymagałoby bloku END, ale przypuszczam, że nadal byłaby to wygrana netto .
skibrianski
Tak, dodanie -n zapisuje tylko 3 znaki (dwa punkty) z blokiem END. Wolę „czystsze” rozwiązanie. Przynajmniej dopóki jedna z pozostałych odpowiedzi nie zbliży się =)
skibrianski
Perl dosłownie otacza while (<>) { ... }cały skrypt, więc nie ma potrzeby blokowania END. Po prostu usuń for(<>){na początku i }na końcu skryptu.
Dennis
3
Będzie jednak działał, o ile usuniesz }koniec skryptu, a nie ten odpowiadający forpętli. Możesz także zapisać jeszcze jeden znak, używając rzeczywistej nowej linii zamiast \n.
Dennis
1

JavaScript ( ES5 ) 191 183 179 168 bajtów

Zakładając, że kod jest uruchamiany w linii poleceń 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]})+[])

Wynik:

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

Tej podkładki można użyć w przeglądarce do symulacji spidermonkey readlinei 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);
};

Nie golfowany:

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
źródło
Pytanie nie mówi, że musisz użyć „stdout” - możesz użyć alertzamiast tego console.logi zapisać niektóre bajty.
Gaurang Tandon
@GaurangTandon Następnie musiałbym połączyć wszystkie linie konturu. Mogę zaktualizować swoją odpowiedź, aby użyć wiersza polecenia spidermonkey, a zamiast tego użyć readlinei printdo rzeczywistego wejścia / wyjścia
nderscore
1

Bash + coreutils, 188 138 bajtów

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
}

Wynik:

$ ./lineupcsv.sh < input.csv 
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
$ 
Cyfrowa trauma
źródło
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

gwykonuje parsowanie - dzieli dane wejściowe na linie i mapuje każdą linię na listę (key,value)par. k, łącząc wszystkie klucze w listę i usuwając duplikaty, tworzy listę ze wszystkimi unikalnymi kluczami, których mogę później użyć do sortowania. Robię to, tworząc „Set” inside main( m(flip(,)"").k$d == [("abc",""),("baz",""),("foo",""),("zxc","")]) dla każdej linii, a następnie pobierając każdą (key,value)parę z linii i umieszczając ją tam, gdzie należy na liście ( foldl). Wiersz 1 z przykładu zwraca [("abc",""),("baz","quux"),("foo","bar"),("zxc","")], które konkatenuję w pojedynczy String ( ",quux,bar,"), konkatenuję z innymi liniami i drukuję.

>>> csv.exe < input.txt
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
Flonk
źródło
0

Python 2.7 - 242 bajty

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)

Zauważ, że druga warstwa wcięcia jest pojedynczym znakiem tabulacji, a nie czterema spacjami takimi jak SE.

Nie golfowany:

#!/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)
podziemny monorail
źródło
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
źródło
0

k4 (40? 51? 70? 46?)

podstawowym wyrażeniem jest

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

to zarówno przyjmuje, jak i zwraca listę ciągów

aby dopasować specyfikację, moglibyśmy zrobić interaktywnie

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

który akceptuje dane wejściowe ze standardowego wejścia i wypisuje dane wyjściowe na standardowe wyjście

w przypadku samodzielnej aplikacji akceptującej dane wejściowe z potoku możemy to zrobić:

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

choć jeśli chcesz rozważyć moje istniejące wcześniej opakowanie k-as-filter, awq.k, jako akceptowalne narzędzie dla tego rodzaju układanek, możemy to zrobić:

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

który ma albo 46 znaków, albo 40, w zależności od tego, jak policzysz cytowanie muszli

Aaron Davies
źródło
Jakie środowisko jest potrzebne do uruchomienia tego? qKomenda? Jest awq.kgdzieś opublikowany?
Cyfrowa trauma
32-bitowy q jest teraz dostępny jako darmowy ze strony kx.com/software-download.php . (kiedyś mieli tylko ograniczoną czasowo wersję próbną za darmo.) hmm, wygląda na to, że awq nie jest nigdzie opublikowany; powinienem coś z tym zrobić.
Aaron Davies,
0

C # - 369

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

Nie golfił

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

Wprowadzanie ciągu testowego

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

Wynik

abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
jzm
źródło
Ciekawe, to jest C #, więc powinien działać w systemie Windows, ale czy tak? (Zobacz moje pytanie CRLFvs. LFczęsto zadawane pytania) Niestety nie mam kopii programu Visual Studio do przetestowania.
durron597
Pierwotnie powinienem był dodać notatkę, ale teraz mam. Tak to działa. Stworzyłem i przetestowałem to w Linqpad. Nie testowałem tego w aplikacji konsolowej, ale nie ma żadnego powodu, dla którego miałby nie działać. Ale aplikacja konsolowa oczywiście dodałaby więcej bajtów do kodu.
jzm