จัดเรียง CSV


12

ข้อมูลทั่วไป:

งานของคุณคือการป้อนข้อมูลkey=valueแบบCSV ในรูปแบบและจัดเรียงให้เป็นระเบียบมากขึ้น (ดูด้านล่าง)

การป้อนข้อมูล:

เสมอผ่านstdin บันทึกจะอยู่ในรูปแบบต่อไปนี้key=value:

foo=bar,baz=quux
abc=123,foo=fubar
baz=qwe,abc=rty,zxc=uiop,foo=asdf
  • จะไม่มีรายการของคีย์ที่เป็นไปได้ล่วงหน้าคุณต้องค้นหาพวกเขาในข้อความที่ป้อน
  • ในตอนท้ายของการป้อนข้อมูลจะถูกส่งสัญญาณโดยEOFการดำเนินการใด ๆEOFที่เหมาะสมสำหรับระบบปฏิบัติการของคุณ

เอาท์พุท:

แถวแรกของเอาต์พุตของคุณจะเป็นรายการของคีย์ทั้งหมดตามลำดับตัวอักษร (แม้ว่าคีย์จะเป็นตัวเลขทั้งหมด) หลังจากนั้นให้พิมพ์แต่ละระเบียนในรูปแบบ CSV เดียวกันกับส่วนหัวของตัวเลขที่เหมาะสมโดยไม่มีคีย์ที่แสดงรายการ ดังนั้นสำหรับตัวอย่างข้างต้นผลลัพธ์ที่ถูกต้องคือ:

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

คำถามที่พบบ่อย:

  • ฉันต้องกังวลเกี่ยวกับรูปแบบการป้อนข้อมูลที่ไม่เหมาะสมหรือไม่?
    • ไม่โปรแกรมของคุณอาจทำสิ่งที่มันต้องการ (โยนข้อยกเว้นเพิกเฉย ฯลฯ ) หากรูปแบบการป้อนข้อมูลไม่ถูกต้องเช่นบรรทัด foo,bar,baz
  • ฉันจะจัดการกับการหลบหนีอักขระพิเศษได้อย่างไร
    • คุณอาจคิดว่าจะไม่มีข้อมูลเพิ่มเติม,หรือ=ในข้อมูลที่ไม่ได้เป็นส่วนหนึ่งของkey=valueรูปแบบ "ไม่มีความหมายพิเศษใด ๆ ในการแข่งขันนี้ (แม้ว่าจะใช้ในรูปแบบ CSV แบบดั้งเดิม) ก็ไม่ได้พิเศษ แต่อย่างใด
    • เส้นควรตรงกับ regex ต่อไปนี้: ^([^=,]+=[^=,]+)(,[^=,]+=[^=,]+)*$
      • ดังนั้นทั้งคีย์และค่าจะตรงกัน [^=,]+
  • สิ่งที่เกี่ยวCRLFกับLF?
    • คุณสามารถเลือกตัวคั่นใดก็ได้ที่เหมาะกับแพลตฟอร์มของคุณ ภาษาส่วนใหญ่จัดการสิ่งนี้โดยไม่มีรหัสการกำหนดพิเศษ
  • ฉันจำเป็นต้องพิมพ์เครื่องหมายจุลภาคต่อท้ายหรือไม่หากไม่พบคอลัมน์สองสามคอลัมน์สุดท้าย
    • ใช่. ดูตัวอย่าง
  • ตัวแยกวิเคราะห์ CSV หรือเครื่องมือภายนอกอื่น ๆ ที่คล้ายกันได้รับอนุญาตหรือไม่
    • ไม่คุณต้องวิเคราะห์ข้อมูลด้วยตัวเอง

15
คำถามที่พบบ่อยเมื่อยังไม่มีใครถามคำถาม :-)
Justin

5
@Quincunx ถ้าฉันถามตัวเองคำถามที่นับ;)
durron597

18
ฉันมีความรู้สึกว่าคำถามที่พบบ่อยทั้งหมดทำงานอย่างไร
Martin Ender

ฉันสามารถใช้เครื่องหมายจุลภาคต่อท้ายในรายการคีย์และค่าของฉันได้หรือไม่ มันจะทำให้รหัสของฉันสั้นมาก ...
PlasmaPower

@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)

De-แข็งแรงเล่นกอล์ฟ:

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 ชี้ให้เห็นด้านล่างคุณสามารถรับได้ถึง 120 + 1 = 121 โดยใช้ -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

โดยทั่วไปสำหรับแต่ละบรรทัดเราแบ่งด้วยเครื่องหมายจุลภาคเพื่อรับรายการคู่ สำหรับแต่ละคู่เราแบ่งตามเครื่องหมายเท่ากับเพื่อรับคีย์และค่า เราตั้งค่าคู่คีย์ / ค่าเป็น% h และ hashref ในระบบ ปุ่มแรกใช้เพื่อกำหนดรายการกุญแจ หลังถูกใช้เพื่อจดจำค่าสำหรับบรรทัดนี้


1
คุณสามารถบันทึกไม่กี่ตัวอักษรโดย: 1. การใช้สวิทช์แทน-n for(<>){...}2. แยกไปรอบ ๆแทนการใช้[, ] chomp3. การเว้นเครื่องหมายอัฒภาคหลังจากวงเล็บปีกกา
เดนนิส

ขอบคุณ @Dennis ฉันได้ดำเนินการตามข้อเสนอแนะ 2 ข้อของคุณแล้ว ฉันยังอาจโยน -n ลงในส่วนผสม แต่ฉันรู้สึกเหมือนเป็นคนเจ้าระเบียบที่ขี้เกียจเกินกว่าจะพิมพ์บนโทรศัพท์ ATM ของเขา :-) นอกจากนี้ยังต้องใช้ END บล็อก แต่ฉันคิดว่ามันจะยังคงเป็นผู้ชนะสุทธิ .
skibrianski

ใช่การเพิ่ม -n จะบันทึกเพียง 3 ตัวอักษร (สองจุด) ด้วยบล็อก END ฉันชอบโซลูชันที่ "บริสุทธิ์กว่า" อย่างน้อยก็จนกว่าหนึ่งในคำตอบอื่น ๆ จะเข้าใกล้ =)
skibrianski

Perl แท้จริงล้อมwhile (<>) { ... }รอบสคริปต์ทั้งหมดจึงไม่จำเป็นต้องมีบล็อก END เพียงลบfor(<>){ที่จุดเริ่มต้นและ}ตอนท้ายของสคริปต์
Dennis

3
มันจะใช้งานได้ตราบใดที่คุณลบ}สคริปต์ในตอนท้ายของสคริปต์ออกไม่ใช่สคริปต์ที่เกี่ยวข้องกับforลูป \nนอกจากนี้คุณสามารถบันทึกหนึ่งถ่านมากขึ้นโดยใช้การขึ้นบรรทัดใหม่ที่เกิดขึ้นจริงแทน
Dennis

1

JavaScript ( ES5 ) 191 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 readlineและ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
    );
}

คำถามไม่ได้บอกว่าคุณต้องใช้ "stdout" - คุณสามารถใช้alertแทนconsole.logและบันทึกบางไบต์
Gaurang Tandon

@GaurangTandon จากนั้นฉันจะต้องเชื่อมต่อทุกเส้นโครงร่าง ฉันอาจปรับปรุงคำตอบของฉันที่จะใช้บรรทัดคำสั่ง spidermonkey และแทนที่จะใช้readlineและprintสำหรับ stdin / out จริง
nderscore


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

gกำลังแยกวิเคราะห์ - มันแยกอินพุตเป็นบรรทัดและแมปแต่ละบรรทัดกับรายการ(key,value)คู่ k, โดยการต่อคีย์ทั้งหมดเข้ากับรายการและลบข้อมูลที่ซ้ำกันออก, สร้างรายการด้วยคีย์เฉพาะทั้งหมดซึ่งฉันสามารถใช้ในการเรียงลำดับในภายหลัง ฉันทำสิ่งนี้โดยการสร้าง "Set" inside main( m(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 ไบต์

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)

โปรดทราบว่าเลเยอร์ที่สองของการเยื้องเป็นอักขระแท็บเดียวไม่ใช่สี่ช่องว่างเช่น SE แสดงผล

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?)

การแสดงออกขั้นพื้นฐานคือ

","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เผยแพร่ที่ไหนสักแห่ง?
Digital Trauma

32 บิตคิวอยู่ในขณะนี้เป็นฟรีแวร์จากkx.com/software-download.php (พวกเขาเคยมีรุ่นทดลองใช้ฟรีที่ จำกัด เวลาเท่านั้น) hmm ดูเหมือนว่า awq ไม่ได้เผยแพร่จริงทุกที่ ฉันควรทำอะไรเกี่ยวกับเรื่องนั้น
Aaron Davies

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

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

ทดสอบอินพุตสตริง

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 ที่จะทำการทดสอบ
durron597

ตอนแรกฉันควรจะเพิ่มบันทึกย่อ แต่ตอนนี้ฉันมีแล้ว ใช่มันใช้งานได้ ฉันได้สร้างและทดสอบใน linqpad แล้ว ฉันยังไม่ได้ทดสอบในแอพคอนโซล แต่ก็ไม่มีเหตุผลใด ๆ ที่มันจะไม่ทำงาน แต่แอพคอนโซลจะเพิ่มจำนวนไบต์ให้กับโค้ดอย่างชัดเจน
jzm
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.