CSVを並べる


12

概要:

あなたの仕事は、CSV入力をkey=value形式で取り、より整理された(以下を参照)方法で並べることです。

入力:

常にstdin経由。レコードは常に次の形式になりますkey=value

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • 事前に可能なキーのリストはありません。入力テキストでそれらを見つける必要があります。
  • 入力の終わりは、OSに適切なEOF実装が何であれ、によって通知さEOFれます。

出力:

出力の最初の行には、すべてのキーのリストがアルファベット順に表示されます(キーがすべて数字の場合でも)。その後、キーをリストせずに、各レコードを同じCSV形式で適切な番号見出しで印刷します。したがって、上記の例の場合、正しい出力は次のようになります。

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

よくある質問:

  • 不適切な形式の入力について心配する必要がありますか?
    • いいえ。プログラムは、入力が正しくフォーマットされていない場合(たとえば、 foo,bar,baz
  • 特殊文字のエスケープを処理するにはどうすればよいですか?
    • あなたは、フォーマットの一部ではない追加のデータ,=データにはないと仮定するかもしれませんkey=value"このコンテストでは特別な意味はありません(従来のCSVではありますが)。また、特別なものでもありません。
    • 行は次の正規表現と一致する必要があります。 ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • したがって、キーと値の両方が一致します [^=,]+
  • 何についてのCRLFLF
    • プラットフォームに適した区切り文字を選択できます。ほとんどの言語は、特別な区切りコードなしでこれを処理します。
  • 最後のいくつかの列が存在しない場合、末尾のコンマを印刷する必要がありますか?
    • はい。例を参照してください。
  • CSVパーサーまたは他の同様の外部ツールは許可されていますか?
    • いいえ。自分でデータを解析する必要があります。

15
誰もまだ質問をしていないときのFAQ。:
ジャスティン14

5
@Quincunx重要な質問を自問すると;)
durron597 14

18
すべてのFAQがどのように機能するかを感じています。
マーティンエンダー14

キーと値のリストに末尾のカンマを含めることはできますか?それは私のコードをずっと短くするでしょう...
PlasmaPower 14

@PlasmaPower質問がわかりません。しかし、あなたのプログラムは正確に与えられた例の入力に対する出力例と一致する必要があります
durron597

回答:


3

GolfScript、64文字

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

このコードはGolfScriptの単純な実装であり、オンラインでサンプルをテストできます

注釈付きコード:

# 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文字、120バイト

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

脱ゴルフ:

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バイト、コマンドラインスイッチなし:

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}

@Dennisが以下で指摘しているように、-nを使用してこれを120 + 1 = 121に取得できます。

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

基本的に、各行について、コンマで分割してペアのリストを取得します。各ペアについて、等号で分割してキーと値を取得します。キーと値のペアを%hとローカルhashrefに設定します。前者は、キーのリストを決定するために使用されます。後者は、この行の値を記憶するために使用されます。


1
次の方法でいくつかの文字を保存できます。1 .の-n代わりにスイッチを使用しfor(<>){...}ます。2. [, ]を使用する代わりに分割しchompます。3.中括弧の後のセミコロンを省略します。
デニス14

ありがとう@デニス。後者の2つの提案を実装しました。私はまだミックスに投入するかもしれませんが、電話ATMで入力するのが面倒な純粋主義者のように感じています:-)また、ENDブロックが必要になりますが、それはまだネットの勝利だと思います。
skibrianski 14

ええ、-nを追加すると、ENDブロックで3文字(2ポイント)しか保存されません。私は「より純粋な」ソリューションを好みます。少なくとも他の回答の1
つがより

Perlは文字通りwhile (<>) { ... }スクリプト全体をラップするため、ENDブロックは必要ありません。スクリプトのfor(<>){最初と}最後で削除するだけです。
デニス14

3
それでも、ループに}対応するスクリプトではなく、スクリプトの最後で削除する限り、それは機能しforます。また、の代わりに実際の改行を使用して、もう1つの文字を保存できます\n
デニス14

1

JavaScriptの(ES5191 183 179 168バイト

コードが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]})+[])

結果:

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

このシムは、シミュレートのSpiderMonkeyのにブラウザで使用することができますreadlineprint

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

ゴルフをしていない:

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

質問は、「stdout」を使用する必要があるとは言っていません。alert代わりに使用して、console.logいくつかのバイトを保存できます。
ゴーランタンドン14

@GaurangTandon次に、すべてのアウトラインを連結する必要があります。私はSpiderMonkeyのコマンドラインを使用し、代わりに使用するように私の答えを更新する可能性がありますreadlineし、print実際の標準入力のために/アウトを
nderscore

1

Bash + coreutils、188 138バイト

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
}

出力:

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

0

ハスケル、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

g解析を実行しています-入力を行に分割し、各行を(key,value)ペアのリストにマップします。k、すべてのキーをリストに連結して重複を削除することで、後でソートに使用できるすべての一意のキーを持つリストを作成します。これを行うには、各行の内部に「セット」を作成しmainm(flip(,)"").k$d == [("abc",""),("baz",""),("foo",""),("zxc","")])、次に(key,value)行からすべてのペアを取得し、リスト内のそれが属する場所に配置します(foldl)。例の1行[("abc",""),("baz","quux"),("foo","bar"),("zxc","")]目はを生成し、これを単一の文字列(",quux,bar,")に連結し、他の行と連結して印刷します。

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

0

Python 2.7-242バイト

ブレ

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)

インデントの2番目のレイヤーは単一のタブ文字であり、SEのような4つのスペースではレンダリングされないことに注意してください。

ゴルフをしていない:

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

基本的な表現は

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

これは、文字列のリストを受け入れて返します

仕様に合わせて、インタラクティブに行うことができます

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

stdinからの入力を受け入れ、stdoutに出力を出力します

パイプからの入力を受け入れるスタンドアロンアプリの場合、次のようにします。

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

私の既存のk-as-filterラッパーawq.kをこの種のパズルの許容できるツールとして検討したい場合は、次のようにします。

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

これは、シェルの引用の乱れを数える方法に応じて、46文字または40文字です。


これを実行するにはどのような環境が必要ですか? qコマンド?されてawq.kどこかに公開?
デジタル外傷14

32ビットのqがkx.com/software-download.phpからフリーウェアとして入手できるようになりました。(以前は無料で期間限定の試用版のみでした。)うーん、awqは実際にはどこにも公開されていないようです。私はそれについて何かをすべきです。
アーロンデイヴィス14

0

C#-369

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

非ゴルフ

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

テスト文字列入力

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

出力

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

好奇心が強い、これはC#なので、Windowsで動作するはずですが、動作しますか?(私の参照CRLF対のLF残念ながら私がテストにVisual Studioのコピーを持っていないのFAQ質問)。
durron597 14

私はもともとメモを追加すべきでしたが、今は追加しました。はい、動作します。linqpadで作成およびテストしました。コンソールアプリではテストしていませんが、機能しない理由はありません。しかし、コンソールアプリは明らかにコードにバイトを追加します。
jzm 14
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.