rimuovere i duplicati in base al valore di un'altra colonna


9

Ho il seguente file:

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

Sto cercando di cercare duplicati e rimuovere la riga con il valore della colonna uguale a true.

come output dovrebbe essere:

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

2
Quindi .. mantenere solo truese è la prima istanza della prima colonna?
DopeGhoti,

1
@RomanPerekhrest Probabilmente perché è una voce uniq ed è stampata "così com'è"
George Vasiliou,

@RomanPerekhrest perché DD, true non è un duplicato, non abbiamo un'altra riga con DD, false.
Hani Gotc,

AA,true AA,false AA,false AA,falseQuale output dovrebbe essere in questo caso? Capisco, quella riga dovrebbe essere rimossa solo se ha duplicati e contiene trueallo stesso tempo. Tutte le falserighe devono rimanere intatte in ogni caso. Cioè, in questo caso, AA, trueverrà rimosso solo. Ma tutte le risposte lasciano solo una riga - AA,false. Semplicemente interessante :)
MiniMax,

Risposte:


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

Per espandere lo script in verticale per la spiegazione:

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 ben spiegato! Hai il mio +1 su questo.
Valentin Bajrami,

14

Versione semplice:

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

"false" ordina in ordine alfabetico prima di "true", e il comando Awk qui mantiene solo la prima riga solo per ciascun valore del primo campo distinto.

Se si desidera mantenere "vero" anziché "falso", invertirlo, passarlo allo stesso comando Awk e invertirlo nuovamente in seguito.


1
inoltre, se l' -uopzione è disponibile,sort input.txt | sort -t, -u -k1,1
Sundeep

2
@Sundeep perché usare due sortchiamate? Perché non solo sort -ut, -k1,1 input.txt ?
terdon

2
@terdon perché -umanterrà la prima riga trovata dal file di input tra i duplicati ... per un determinato caso, l'input deve essere ordinato prima che -upossa essere applicato ... per esempio: AA,trueverrà stampato invece che AA,falsepoiché appare per primo nel campione dato .. stesso motivo per cui awk -F, '!a[$1]++'da soli non risolverà questo problema
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

Strutture dati:

  • Hash le %hcui chiavi sono i primi campi (AAA, BBB, CCC, ecc.) Ei valori corrispondenti sono numeri che indicano l'ordine in cui sono state rilevate le chiavi. Pertanto, ad es. Chiave AAA => 0, chiave BBB => 1, chiave CCC => 2.
  • Matrice i @hcui elementi sono linee contenute nell'ordine di stampa. Quindi, se nei dati vengono rilevati sia vero che falso, il valore falso andrà nell'array. OTW, se esiste un tipo di dati, sarebbe presente.

Un altro modo è usare 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, il codice equivalente POSIX per il precedente codice GNU-sed è elencato di seguito:

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

Spiegazione

  • In questo metodo memorizziamo il risultato da stampare infine nello spazio di attesa.
  • Per ogni riga letta, aggiungiamo lo spazio di mantenimento allo spazio del modello per l'esame della linea corrente di fronte allo stato esistente dello spazio di attesa.
  • Ora possono succedere 5 cose durante questo confronto:
    • a) La linea corrente corrisponde da qualche parte nella linea di attesa e false: false.
      • [AZIONE] Poiché viene rilevato lo stesso falso stato, non fare nulla.
    • b) La linea corrente corrisponde da qualche parte nella linea di attesa e true: true.
      • [AZIONE] Poiché viene trovato lo stesso vero stato, non fare nulla.
    • c) La linea corrente corrisponde da qualche parte nella linea di attesa e true: false.
      • [AZIONE] Poiché esiste già un falso stato, non fare nulla.
    • d) La linea corrente corrisponde da qualche parte nella linea di attesa e false: true.
      • [AZIONE] Ciò comporta un po 'di lavoro, in quanto dobbiamo sostituire la falsa linea nella stessa posizione esatta in cui si trova il vero.
    • e) La linea corrente NON corrisponde in alcun punto della linea di attesa.
      • [AZIONE] Sposta la riga corrente alla fine.

risultati

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

3

Per ogni riga di input, memorizza il valore del secondo campo nell'array associativo a(usando il primo campo come chiave dell'array) SOLO se non abbiamo già memorizzato il valore falseper quella chiave. Utilizzare ,sia per il separatore di campo di input che di output. Stampa l'array dopo aver letto tutte le righe di input.

$ 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

La differenza significativa tra questa e la versione di DopeGhoti è che questa versione non si preoccupa affatto del valore di $2, si preoccupa solo del valore, se presente, di a[$1].


1

sortSoluzione a due passaggi

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

Primo sortpassaggio cluster di record per campo 1con falserecord precedenti trueper ogni blocco di record che condividono un 1valore di campo comune . Il secondo sortpassaggio è impostato per fornire un record per ogni valore distinto all'interno del campo per 1gentile concessione del -u. Poiché -uimplica un ordinamento stabile, l'unico record così prodotto è il primo record incontrato per ciascun valore distinto all'interno del campo 1- che è un record con falsenel secondo campo a causa del lavoro svolto dal primo sortpassaggio

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.