supprimer les doublons en fonction de la valeur d'une autre colonne

9

J'ai le fichier suivant:

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

J'essaie de rechercher des doublons et de supprimer la ligne dont la valeur de colonne est égale à true.

en sortie, il devrait être:

AA,false
BB,false
CC,false
DD,true
Hani Gotc
la source
2
Donc .. ne garder que truesi c'est la première instance de la première colonne?
DopeGhoti
1
@RomanPerekhrest Probablement parce qu'il s'agit d'une entrée uniq et est imprimée "telle quelle"
George Vasiliou
@RomanPerekhrest parce que DD, true n'est pas un doublon, nous n'avons pas d'autre ligne avec DD, false.
Hani Gotc
AA,true AA,false AA,false AA,falseQuelle sortie devrait être dans ce cas? Je comprends que cette ligne ne doit être supprimée que si elle contient des doublons et contient trueen même temps. Dans tous les falsecas, toutes les lignes doivent rester intactes. Autrement dit, dans ce cas, seul AA, truesera supprimé. Mais toutes les réponses ne laissent qu'une seule ligne - AA,false. Juste intéressant :)
MiniMax

Réponses:

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

Pour développer le script verticalement pour explication:

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
la source
@DopeGhoti bien expliqué! Vous avez mon +1 à ce sujet.
Valentin Bajrami
14

Version simple:

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

"false" trie alphabétiquement avant "true", et la commande Awk ici ne conserve que la première ligne pour chaque première valeur de champ distincte.

Si vous souhaitez conserver "true" au lieu de "false", inversez-le, passez-le à la même commande Awk et inversez-le à nouveau par la suite.

Caractère générique
la source
1
aussi, si l' -uoption est disponible,sort input.txt | sort -t, -u -k1,1
Sundeep
2
@Sundeep pourquoi utiliser deux sortappels? Pourquoi pas juste sort -ut, -k1,1 input.txt ?
terdon
2
@terdon car -uconservera la première ligne trouvée dans le fichier d'entrée parmi les doublons ... pour un cas donné, l'entrée doit être triée avant de -upouvoir être appliquée ... par exemple: AA,truesera imprimée au lieu de AA,falsecar elle apparaît en premier dans l'échantillon donné. même raison pour laquelle awk -F, '!a[$1]++'seul ne résoudra pas ce problème
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

Structures de données:

  • Hash %hdont les clés sont des premiers champs (AAA, BBB, CCC, etc.) et les valeurs correspondantes sont des nombres indiquant l'ordre dans lequel les clés ont été rencontrées. Ainsi, par exemple, clé AAA => 0, clé BBB => 1, clé CCC => 2.
  • Tableau @hdont les éléments sont des lignes contenues dans l'ordre d'impression. Donc, si à la fois vrai et faux sont trouvés dans les données, la fausse valeur ira dans le tableau. OTW, s'il y a un type de données, alors ce serait présent.

Une autre façon utilise 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, le code équivalent POSIX pour le code GNU-sed ci-dessus est répertorié ci-dessous:

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

Explication

  • Dans cette méthode, nous stockons le résultat pour qu'il soit finalement imprimé dans l'espace d'attente.
  • Pour chaque ligne lue, nous ajoutons l'espace de maintien à l'espace de motif pour examiner la ligne actuelle par rapport à l'état existant de l'espace de maintien.
  • Maintenant, 5 choses peuvent éventuellement se produire pendant cette comparaison:
    • a) La ligne actuelle correspond quelque part dans la ligne d'attente et false: false.
      • [ACTION] Puisque le même faux état est trouvé, alors ne faites rien.
    • b) La ligne actuelle correspond quelque part dans la ligne d'attente et true: true.
      • [ACTION] Puisque le même vrai état est trouvé, alors ne faites rien.
    • c) La ligne actuelle correspond quelque part dans la ligne d'attente et true: false.
      • [ACTION] Puisqu'un faux état existe déjà, ne faites rien.
    • d) La ligne actuelle correspond quelque part dans la ligne d'attente et false: true.
      • [ACTION] Cela implique un certain travail, en ce que nous devons remplacer la fausse ligne à la même position exacte où se trouve le vrai.
    • e) La ligne actuelle ne correspond à aucun endroit de la ligne d'attente.
      • [ACTION] Déplacer la ligne actuelle jusqu'à la fin.

Résultats

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

la source
3

Pour chaque ligne d'entrée, stockez la valeur du deuxième champ dans un tableau associatif a(en utilisant le premier champ comme clé du tableau) UNIQUEMENT si nous n'avons pas déjà stocké la valeur falsede cette clé. À utiliser ,pour le séparateur de champ d'entrée et de sortie. Imprimez le tableau après avoir lu toutes les lignes d'entrée.

$ 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 différence significative entre cela et la version de DopeGhoti est que cette version ne se soucie pas du tout de la valeur de $2, elle ne se soucie que de la valeur, le cas échéant, de a[$1].

cas
la source
1

sortSolution en deux passes

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

sortPassez d' abord les enregistrements de clusters par champ 1avec les falseenregistrements précédant truepour chaque bloc d'enregistrements partageant une 1valeur de champ commune . La deuxième sortpasse est configurée pour produire un enregistrement pour chaque valeur distincte dans le champ 1avec la permission du -u. Puisque -uimplique un tri stable, le seul enregistrement ainsi produit est le premier enregistrement rencontré pour chaque valeur distincte dans le champ 1- qui est un enregistrement avec falsedans le deuxième champ en raison du travail effectué par la première sortpasse

iruvar
la source