Alignez le CSV

12

Aperçu:

Votre travail consiste à prendre l'entrée CSV au key=valueformat et à l'aligner de manière plus organisée (voir ci-dessous).

Contribution:

Toujours via stdin . Les enregistrements seront toujours sous la forme suivante key=value:

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • Il n'y aura pas de liste de clés possibles à l'avance, vous devez les trouver dans le texte saisi.
  • La fin de l'entrée sera signalée par EOF, quelle que soit l'implémentation de EOFappropriée à votre système d'exploitation.

Production:

La première ligne de votre sortie sera une liste de toutes les clés, par ordre alphabétique (même si les clés sont toutes des nombres). Après cela, imprimez chaque enregistrement dans le même format CSV l'en-tête de numéro approprié, sans les clés répertoriées. Ainsi, pour l'exemple ci-dessus, la sortie correcte serait:

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

FAQ:

  • Dois-je m'inquiéter d'une entrée mal formatée?
    • Non. Votre programme peut faire ce qu'il veut (lever une exception, ignorer, etc.) si l'entrée n'est pas correctement formatée, par exemple une ligne de foo,bar,baz
  • Comment gérer les caractères spéciaux d'échappement?
    • Vous pouvez supposer qu'il n'y aura pas de données supplémentaires ,ou =dans les données qui ne font pas partie du key=valueformat. "n'a pas de signification particulière dans ce concours (même si c'est le cas en CSV traditionnel). n'est pas spécial non plus.
    • Les lignes doivent correspondre à l'expression régulière suivante: ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • Par conséquent, les clés et les valeurs correspondront [^=,]+
  • Et CRLFcontre LF?
    • Vous pouvez choisir le délimiteur approprié à votre plateforme. La plupart des langues gèrent cela sans code de délimitation spécial.
  • Dois-je imprimer des virgules de fin si les dernières colonnes n'existent pas?
    • Oui. Voir l'exemple.
  • Les analyseurs CSV ou autres outils externes similaires sont-ils autorisés?
    • Non. Vous devez analyser les données vous-même.
durron597
la source
15
FAQ quand personne n'a encore posé de questions. :-)
Justin
5
@Quincunx Si je me pose la question qui compte;)
durron597
18
J'ai l'impression que c'est ainsi que fonctionnent toutes les FAQ.
Martin Ender
Puis-je avoir une virgule de fin dans ma liste de clés et de valeurs? Cela rendrait mon code beaucoup plus court ...
PlasmaPower
@PlasmaPower Je ne comprends pas la question; cependant, votre programme doit correspondre exactement à l'exemple de sortie pour l'exemple donné d'entrée
durron597

Réponses:

3

GolfScript, 64 caractères

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

Le code est une implémentation simple dans GolfScript, vous pouvez tester l'exemple en ligne .

Code annoté:

# 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
la source
2

Perl 6: 119 caractères, 120 octets

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
la source
2

perl, 129/121

129 octets, pas de commutateurs de ligne de commande:

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}

Comme le souligne @Dennis ci-dessous, vous pouvez obtenir ceci à 120 + 1 = 121 en utilisant -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

Fondamentalement, pour chaque ligne, nous nous séparons par des virgules pour obtenir la liste des paires. Pour chaque paire, nous nous séparons par le signe égal pour obtenir la clé et la valeur. Nous définissons la paire clé / valeur dans% h et une référence de hachage locale. Le premier est utilisé pour déterminer la liste des clés. Ce dernier est utilisé pour mémoriser les valeurs de cette ligne.

skibrianski
la source
1
Vous pouvez enregistrer quelques caractères en: 1. utilisant le -ncommutateur au lieu de for(<>){...}. 2. Se diviser [, ]au lieu d'utiliser chomp. 3. Omettre le point-virgule après les accolades.
Dennis
Merci @Dennis. J'ai mis en œuvre les 2 dernières de vos suggestions. Je peux encore lancer -n dans le mix mais je me sens comme un puriste qui est trop paresseux pour taper sur son téléphone ATM :-) Cela nécessiterait également un bloc END, mais je suppose que ce serait toujours une victoire nette .
skibrianski
Oui, l'ajout de -n enregistre seulement 3 caractères (deux points) avec le bloc END. Je préfère la solution "plus pure". Au moins jusqu'à ce que l'une des autres réponses se rapproche =)
skibrianski
Perl enveloppe littéralement while (<>) { ... }tout le script, donc il n'y a pas besoin de bloc END. Supprimez simplement for(<>){au début et }à la fin du script.
Dennis
3
Cela fonctionnera néanmoins, tant que vous supprimez le }à la fin du script, pas celui correspondant à la forboucle. En outre, vous pouvez enregistrer un caractère de plus en utilisant une nouvelle ligne réelle au lieu de \n.
Dennis
1

JavaScript ( ES5 ) 191 183 179 168 octets

En supposant que le code est exécuté en ligne de commande 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]})+[])

Résultat:

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

Cette cale peut être utilisée dans un navigateur pour simuler les spidermonkey readlineet 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);
};

Non 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
la source
La question ne dit pas que vous devez utiliser "stdout" - vous pouvez utiliser alertà la place console.loget enregistrer ainsi quelques octets.
Gaurang Tandon
@GaurangTandon Ensuite, je devrais concaténer toutes les lignes de contour. Je pourrais mettre à jour ma réponse pour utiliser la ligne de commande spidermonkey et utiliser à la place readlineet printpour stdin / out
réel
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
}

Production:

$ ./lineupcsv.sh < input.csv 
abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
$ 
Traumatisme numérique
la source
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

gfait l'analyse - il divise l'entrée en lignes et mappe chaque ligne à une liste de (key,value)paires. k, en concaténant toutes les clés dans une liste et en supprimant les doublons, crée une liste avec toutes les clés uniques que je pourrai ensuite utiliser pour le tri. Je le fais en créant un "Set" à l'intérieur main( m(flip(,)"").k$d == [("abc",""),("baz",""),("foo",""),("zxc","")]) pour chaque ligne, puis en prenant chaque (key,value)paire d'une ligne et en la mettant à sa place dans la liste ( foldl). La ligne 1 de l'exemple donne [("abc",""),("baz","quux"),("foo","bar"),("zxc","")], que je concatène en une seule chaîne ( ",quux,bar,"), concatène avec les autres lignes et imprime.

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

Python 2.7 - 242 octets

saigner

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)

Notez que la deuxième couche d'indentation est un caractère de tabulation unique, et non quatre espaces comme SE le rend.

Non 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)
métro monorail
la source
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
la source
0

k4 (40? 51? 70? 46?)

l'expression de base est

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

cela accepte et renvoie une liste de chaînes

pour correspondre à la spécification, nous pourrions faire de manière interactive

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

qui accepte l'entrée de stdin et imprime la sortie sur stdout

pour une application autonome acceptant les entrées d'un tuyau, nous pourrions faire ceci:

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

bien que si vous êtes prêt à considérer mon wrapper k-as-filter préexistant, awq.k, comme un outil acceptable pour ce genre de puzzle, alors nous pouvons le faire:

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

qui est soit 46 caractères soit 40, selon la façon dont vous comptez les disputes entre guillemets

Aaron Davies
la source
Quel type d'environnement est nécessaire pour exécuter cela? qcommander? Est awq.kpublié quelque part?
Digital Trauma
32 bits q est désormais disponible en tant que logiciel gratuit sur kx.com/software-download.php . (ils n'avaient auparavant qu'une version d'essai limitée dans le temps.) hmm, on dirait que awq n'est en fait publié nulle part; je devrais faire quelque chose à ce sujet.
Aaron Davies
0

C # - 369

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

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

Tester l'entrée de chaîne

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

Production

abc,baz,foo,zxc
,quux,bar,
123,,fubar,
rty,qwe,asdf,uiop
jzm
la source
Juste curieux, c'est C # donc ça devrait fonctionner dans windows, mais est-ce le cas? (Voir ma question CRLFvs LFFAQ) Malheureusement, je n'ai pas de copie de Visual Studio pour tester.
durron597
J'aurais dû à l'origine ajouter la note, mais je l'ai maintenant. Oui cela fonctionne. Je l'ai créé et testé dans linqpad. Je ne l'ai pas testé dans l'application console, mais il n'y a aucune raison pour que cela ne fonctionne pas. Mais une application console ajouterait évidemment plus d'octets au code.
jzm