Entfernen Sie Duplikate basierend auf dem Wert einer anderen Spalte


9

Ich habe die folgende Datei:

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

Ich versuche nach Duplikaten zu suchen und die Zeile zu entfernen, deren Spaltenwert gleich ist true.

als Ausgabe sollte es sein:

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

2
Also .. nur behalten, truewenn es die erste Instanz der ersten Spalte ist?
DopeGhoti

1
@ RomanPerekhrest Wahrscheinlich, weil es sich um einen einzigartigen Eintrag handelt, der "so wie er ist" gedruckt wird
George Vasiliou

@RomanPerekhrest weil DD, true kein Duplikat ist, haben wir keine weitere Zeile mit DD, false.
Hani Gotc

AA,true AA,false AA,false AA,falseWelche Ausgabe sollte in diesem Fall sein? Ich verstehe, dass diese Zeile nur entfernt werden sollte, wenn sie doppelt vorhanden ist und gleichzeitig enthält true. Alle falseZeilen sollten auf jeden Fall unberührt bleiben. Das heißt, in diesem Fall wird nur AA, trueentfernt. Aber alle Antworten lassen nur eine Zeile - AA,false. Einfach interessant :)
MiniMax

Antworten:


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

So erweitern Sie das Skript zur Erklärung vertikal:

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 gut erklärt! Du hast meine +1 dafür.
Valentin Bajrami

14

Einfache Version:

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

"false" wird alphabetisch vor "true" sortiert, und der Befehl "Awk" behält hier nur die erste Zeile für jeden einzelnen ersten Feldwert bei.

Wenn Sie "true" anstelle von "false" beibehalten möchten, sortieren Sie es in umgekehrter Reihenfolge, übergeben Sie es an denselben Awk-Befehl und sortieren Sie es anschließend erneut in umgekehrter Reihenfolge.


1
auch, wenn -uOption verfügbar ist,sort input.txt | sort -t, -u -k1,1
Sundeep

2
@Sundeep warum zwei sortAnrufe verwenden? Warum nicht einfach sort -ut, -k1,1 input.txt ?
Terdon

2
@terdon, weil -udie erste Zeile aus der Eingabedatei unter den Duplikaten beibehalten wird ... für einen bestimmten Fall muss die Eingabe sortiert werden, bevor -usie angewendet werden kann ... zum Beispiel: AA,truewird gedruckt, anstatt, AA,falseda sie in einem bestimmten Beispiel zuerst erscheint. Der gleiche Grund, warum awk -F, '!a[$1]++'allein dieses Problem nicht lösen kann
Sundeep

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

Datenstrukturen:

  • Hash, %hdessen Schlüssel erste Felder sind (AAA, BBB, CCC usw.) und entsprechende Werte sind Zahlen, die die Reihenfolge angeben, in der die Schlüssel gefunden wurden. So ist zB Schlüssel AAA => 0, Schlüssel BBB => 1, Schlüssel CCC => 2.
  • Array, @hdessen Elemente Zeilen sind, die in der Reihenfolge des Druckens enthalten sind. Wenn also sowohl wahr als auch falsch in Daten gefunden werden, wird der falsche Wert in das Array übernommen. OTW, wenn es einen Datentyp gibt, dann wäre dieser vorhanden.

Ein anderer Weg ist die Verwendung von 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, der POSIX-äquivalente Code für den obigen GNU-sed-Code ist unten aufgeführt:

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

Erläuterung

  • Bei dieser Methode speichern wir das Ergebnis, das endgültig gedruckt werden soll, im Haltebereich.
  • Für jede gelesene Zeile hängen wir den Haltebereich an den Musterraum an, um die aktuelle Zeile im Hinblick auf den vorhandenen Zustand des Haltebereichs zu untersuchen.
  • Jetzt können bei diesem Vergleich möglicherweise 5 Dinge passieren:
    • a) Die aktuelle Zeile stimmt irgendwo in der Haltezeile überein & false: false.
      • [AKTION] Da derselbe falsche Zustand gefunden wird, tun Sie nichts.
    • b) Die aktuelle Zeile stimmt mit der Haltelinie überein & true: true.
      • [AKTION] Da derselbe wahre Zustand gefunden wird, tun Sie nichts.
    • c) Die aktuelle Zeile stimmt mit der Haltelinie überein & true: false.
      • [AKTION] Da bereits ein falscher Zustand vorliegt, tun Sie nichts.
    • d) Die aktuelle Zeile stimmt irgendwo in der Haltelinie überein & false: true.
      • [ACTION] Dies erfordert einige Arbeit, da wir die falsche Linie genau an der Stelle ersetzen müssen, an der sich die wahre befindet.
    • e) Die aktuelle Leitung stimmt NICHT mit der Haltelinie überein.
      • [AKTION] Verschieben Sie die aktuelle Zeile bis zum Ende.

Ergebnisse

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

3

Speichern Sie für jede Eingabezeile den Wert des zweiten Felds NUR im assoziativen Array a(wobei das erste Feld als Schlüssel des Arrays verwendet wird), wenn wir den Wert für diesen Schlüssel noch nicht gespeichert haben . Wird sowohl für das Eingabe- als auch für das Ausgabefeldtrennzeichen verwendet. Drucken Sie das Array aus, nachdem Sie alle Eingabezeilen gelesen haben.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

Der wesentliche Unterschied zwischen dieser und der DopeGhoti-Version besteht darin, dass sich diese Version überhaupt nicht um den Wert von $2kümmert, sondern nur um den Wert von, falls vorhanden a[$1].


1

Zwei-Pass- sortLösung

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

Der erste sortDurchgang gruppiert Datensätze nach Feldern, 1wobei falseDatensätze truefür jeden Datensatzblock vorangestellt sind, die einen gemeinsamen Feldwert 1haben. Der zweite sortDurchgang ist so eingerichtet, dass ein Datensatz für jeden bestimmten Wert innerhalb des Feldes mit 1freundlicher Genehmigung von erstellt wird -u. Da dies -ueine stabile Sortierung impliziert, ist der eine Datensatz, der auf diese Weise erhalten wird, der erste Datensatz, der für jeden einzelnen Wert innerhalb des Felds angetroffen wird 1- was falseaufgrund der im ersten sortDurchgang geleisteten Arbeit ein Datensatz im zweiten Feld ist

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.