ลบรายการที่ซ้ำตามค่าของคอลัมน์อื่น


9

ฉันมีไฟล์ต่อไปนี้:

AA,true
AA,false
BB,false
CC,false
BB,true
DD,true

trueฉันพยายามที่จะมองหาที่ซ้ำกันและลบบรรทัดที่มีค่าเท่ากับคอลัมน์

ตามที่ควรเป็น:

AA,false
BB,false
CC,false
DD,true

2
ดังนั้น .. โปรดเก็บไว้trueว่ามันเป็นอินสแตนซ์แรกของคอลัมน์แรกหรือไม่
DopeGhoti

1
@ RomanPerekhrest อาจเป็นเพราะรายการเดียวและพิมพ์ "ตามที่เป็น"
George Vasiliou

@RomanPerekhrest เนื่องจาก DD จริงไม่ซ้ำกันเราไม่มีบรรทัดอื่นที่มี DD เป็นเท็จ
Hani Gotc

AA,true AA,false AA,false AA,falseสิ่งที่ควรจะเป็นในกรณีนี้ ฉันเข้าใจว่าแถวนั้นควรถูกลบออกหากว่ามันมีการทำซ้ำและมีtrueในเวลาเดียวกัน falseแถวทั้งหมดควรไม่ถูกแตะต้องในทุกกรณี นั่นคือในกรณีนี้AA, trueจะถูกลบออกเท่านั้น แต่คำตอบทั้งหมดเหลือเพียงบรรทัดเดียว - AA,false. น่าสนใจ :):
MiniMax

คำตอบ:


9
awk -F, '$2 == "false" {data[$1]=$2 } $2=="true" { if ( data[$1]!="false" ) { data[$1]=$2 } } END { OFS=","; for (item in data) { print item,data[item] }}' input

หากต้องการขยายสคริปต์ในแนวตั้งเพื่อดูคำอธิบาย:

BEGIN {
   FS=","         # Set the input separator; this is what -F, does.
}
$2 == "false" {    # For any line whose second field is "false", we
   data[$1]=$2     # will use that value no matter what.
}
$2=="true" {                    # For lines whose second field is "true",
   if ( data[$1]!="false" ) {   # only keep if if we haven't yet seen a
      data[$1]=$2               # "false"
   }
}
END {                           # Now that we have tabulated our data, we
   OFS=","                      # can print it out by iterating through 
   for (item in data) {         # the array we created.
      print item,data[item]
   }
}

@DopeGhoti อธิบายได้ดี! คุณได้ +1 ของฉันจากสิ่งนี้
Valentin Bajrami

14

รุ่นง่าย:

sort input.txt | awk -F, '!a[$1]++'

"false" เรียงลำดับตัวอักษรก่อนหน้า "true" และคำสั่ง Awk ที่นี่จะเก็บแถวแรกไว้เฉพาะสำหรับแต่ละค่าเขตข้อมูลแรกเท่านั้น

หากคุณต้องการที่จะเก็บ "ความจริง" แทนที่จะเป็น "เท็จ" ให้เรียงกลับมันส่งผ่านไปยังคำสั่ง Awk เดียวกันและกลับเรียงลำดับอีกครั้งในภายหลัง


1
นอกจากนี้หากมี-uตัวเลือกให้ใช้งานด้วยsort input.txt | sort -t, -u -k1,1
Sundeep

2
@Seepeep ทำไมต้องใช้สองsortสาย? ทำไมไม่เพียงsort -ut, -k1,1 input.txt ?
terdon

2
@terdon เพราะ-uจะเก็บบรรทัดแรกที่พบจากอินพุตไฟล์ซ้ำกัน ... สำหรับเคสที่กำหนดอินพุตจะต้องเรียงลำดับก่อนที่จะ-uสามารถนำไปใช้ ... สำหรับ ex: AA,trueจะถูกพิมพ์แทนAA,falseเนื่องจากจะปรากฏเป็นครั้งแรกในตัวอย่างที่กำหนด .. เหตุผลเดียวกันว่าทำไมawk -F, '!a[$1]++'คนเดียวไม่สามารถแก้ปัญหานี้ได้
ซันเดพ

5
perl -F, -lane '
   exists $h{$F[0]} or $h[$h{$F[0]}=@h]=$_;
   $h=$_; /,false$/ or $_=$h for $h[$h{$F[0]}];
   END{ print for @h; }
' duplicates.file

โครงสร้างข้อมูล:

  • แฮช%hที่มีคีย์คือฟิลด์แรก (AAA, BBB, CCC ฯลฯ ) และค่าที่เกี่ยวข้องคือตัวเลขที่บอกลำดับที่พบคีย์ ดังนั้นเช่นคีย์ AAA => 0, คีย์ BBB => 1, คีย์ CCC => 2
  • Array @hที่มีองค์ประกอบคือเส้นที่มีอยู่ในลำดับการพิมพ์ ดังนั้นหากพบทั้งข้อมูลจริงและเท็จค่าเท็จจะเข้าไปในอาร์เรย์ OTW หากมีข้อมูลประเภทใดประเภทหนึ่งก็จะมีอยู่

อีกวิธีคือใช้ GNU sed:

sed -Ee '
   G
   /^([^,]*),(false|true)\n(.*\n)?\1,\2(\n|$)/ba
   /^([^,]*)(,true)\n(.*\n)?\1,false(\n|$)/ba
   /^([^,]*)(,false)\n((.*\n)?)\1,true(\n|$)/{
      s//\3\1\2\5/;h;ba
   }
   s/([^\n]*)\n(.*)$/\2\n\1/;s/^\n*//
   h;:a;$!d;g
' duplicates.file

FWIW, โค้ดเทียบเท่า POSIX สำหรับโค้ด GNU-sed ด้านบนแสดงอยู่ด้านล่าง:

sed -e '
   G

   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(false\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2$/ba
   /^\([^,]*\),\(true\)\n\(.*\n\)\{0,1\}\1,\2\n/ba

   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false$/ba
   /^\([^,]*\),true\n\(.*\n\)\{0,1\}\1,false\n/ba

   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true$/{
      s//\3\1\2/
      h
      ba
   }
   /^\([^,]*\)\(,false\)\n\(\(.*\n\)\{0,1\}\)\1,true\n/{
      s//\3\1\2\n/
      h
      ba
   }

   y/\n_/_\n/
   s/\([^_]*\)_\(.*\)$/\2_\1/;s/^_*//
   y/\n_/_\n/

   h;:a;$!d;g
' duplicates.file

คำอธิบาย

  • ในวิธีนี้เราจะเก็บผลลัพธ์ที่จะพิมพ์ในพื้นที่พักไว้
  • สำหรับการอ่านทุกบรรทัดเราจะต่อท้ายพื้นที่พักไว้กับพื้นที่รูปแบบสำหรับการตรวจสอบของบรรทัดปัจจุบันเผชิญหน้ากับสถานะปัจจุบันของพื้นที่พัก
  • ตอนนี้ 5 สิ่งที่อาจเกิดขึ้นระหว่างการเปรียบเทียบนี้:
    • a) บรรทัดปัจจุบันตรงกับที่ใดที่หนึ่งในสายพัก & false: false
      • [การกระทำ] เนื่องจากพบสถานะเท็จเดียวกันจากนั้นจึงไม่ทำอะไรเลย
    • b) บรรทัดปัจจุบันตรงกับที่ใดที่หนึ่งในบรรทัดที่พักไว้ & จริง: จริง
      • [การกระทำ] เนื่องจากพบสภาพที่แท้จริงเหมือนกันดังนั้นอย่าทำอะไรเลย
    • c) บรรทัดปัจจุบันตรงกับที่ใดที่หนึ่งในบรรทัดที่พักไว้ & true: false
      • [การกระทำ] เนื่องจากสถานะเท็จมีอยู่แล้วไม่ต้องทำอะไรเลย
    • d) บรรทัดปัจจุบันตรงกับที่ใดที่หนึ่งในบรรทัดที่พักไว้ & false: จริง
      • [การดำเนินการ] สิ่งนี้เกี่ยวข้องกับงานบางอย่างเนื่องจากเราจำเป็นต้องแทนที่สายปลอมในตำแหน่งเดียวกันกับที่ตั้งของจริง
    • e) สายปัจจุบันไม่ตรงกับที่ใดก็ได้ในสายพัก
      • [การกระทำ] ย้ายบรรทัดปัจจุบันไปยังจุดสิ้นสุด

ผล

AA,false
BB,false
CC,false
DD,true

3

สำหรับแต่ละบรรทัดอินพุตให้เก็บค่าของฟิลด์ที่สองในอาเรย์แบบเชื่อมโยงa(ใช้ฟิลด์แรกเป็นคีย์ของอาเรย์) เฉพาะเมื่อเรายังไม่ได้เก็บค่าfalseสำหรับคีย์นั้น ใช้,สำหรับตัวคั่นฟิลด์อินพุตและเอาต์พุต พิมพ์อาร์เรย์หลังจากเราอ่านบรรทัดอินพุตทั้งหมด

$ awk -F, -v OFS=, 'a[$1] != "false" { a[$1] = $2 };
                    END { for (i in a) {print i,a[i]} }' truefalse.txt
AA,false
BB,false
CC,false
DD,true

ความแตกต่างที่สำคัญระหว่างนี้และรุ่น DopeGhoti คือว่ารุ่นนี้ไม่สนใจที่เกี่ยวกับค่าของ$2มันเพียงใส่ใจเกี่ยวกับค่าถ้าใด ๆ a[$1]ของ


1

sortโซลูชันสองรอบ

sort -k1,1 -k2,2 -t, file | sort -k1,1 -t, -u

First sortpass clusters ระเบียนตามเขตข้อมูลที่1มีfalseระเบียนนำหน้าtrueสำหรับแต่ละบล็อกของระเบียนที่ใช้1ค่าเขตข้อมูลร่วมกัน ที่สองsortผ่านการตั้งค่าให้ผลผลิตหนึ่งระเบียนสำหรับแต่ละค่าที่แตกต่างกันภายในเขตมารยาท1 -uเนื่องจาก-uหมายถึงการเรียงลำดับที่เสถียรหนึ่งระเบียนจึงให้ผลคือระเบียนแรกที่พบสำหรับแต่ละค่าที่แตกต่างภายในเขตข้อมูล1- ซึ่งเป็นระเบียนที่มีfalseในเขตข้อมูลที่สองเนื่องจากงานที่ทำโดยsortผ่านครั้งแรก

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