eliminar duplicados en función del valor de otra columna

9

Tengo el siguiente archivo:

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

Estoy tratando de buscar duplicados y eliminar la línea que tiene el valor de la columna igual true.

como salida debería ser:

AA,false
BB,false
CC,false
DD,true
Hani Gotc
fuente
2
Entonces ... ¿solo se mantiene truesi es la primera instancia de la primera columna?
DopeGhoti
1
@RomanPerekhrest Probablemente porque es una entrada única y está impresa "como está"
George Vasiliou
@RomanPerekhrest porque DD, verdadero no es un duplicado, no tenemos otra línea con DD, falso.
Hani Gotc
AA,true AA,false AA,false AA,false¿Qué salida debería ser en este caso? Entiendo que esa fila debe eliminarse solo si tiene duplicados y contiene trueal mismo tiempo. Todas las falsefilas deben permanecer intactas en cualquier caso. Es decir, en este caso, solo AA, truese eliminará. Pero todas las respuestas dejan solo una línea - AA,false. Simplemente interesante :)
MiniMax

Respuestas:

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

Para expandir el script verticalmente para explicación:

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
fuente
@DopeGhoti bien explicado! Tienes mi +1 en esto.
Valentin Bajrami
14

Versión simple:

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

"falso" se ordena alfabéticamente antes de "verdadero", y el comando Awk aquí solo mantiene la primera fila solo para cada primer valor de campo distinto.

Si desea mantener "verdadero" en lugar de "falso", ordénelo en reversa, páselo al mismo comando Awk y ordénelo en reversa nuevamente después.

Comodín
fuente
1
también, si la -uopción está disponible,sort input.txt | sort -t, -u -k1,1
Sundeep
2
@Sundeep ¿por qué usar dos sortllamadas? ¿Por qué no solo sort -ut, -k1,1 input.txt ?
terdon
2
@terdon porque -uretendrá la primera línea encontrada del archivo de entrada entre los duplicados ... para un caso dado, la entrada debe clasificarse antes de -upoder aplicarse ... por ejemplo: AA,truese imprimirá en lugar de AA,falsecomo aparece primero en la muestra dada ... misma razón por la que awk -F, '!a[$1]++'solo no resolverá este 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

Estructuras de datos:

  • Hash %hcuyas claves son los primeros campos (AAA, BBB, CCC, etc.) y los valores correspondientes son números que indican el orden en que se encontraron las claves. Así, por ejemplo, clave AAA => 0, clave BBB => 1, clave CCC => 2.
  • Matriz @hcuyos elementos son líneas contenidas en el orden de impresión. Entonces, si se encuentran tanto verdadero como falso en los datos, entonces el valor falso irá a la matriz. OTW, si hay un tipo de datos, entonces estaría presente.

Otra forma es usar 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, el código POSIX equivalente para el código GNU-sed anterior se enumera a continuación:

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

Explicación

  • En este método almacenamos el resultado para que finalmente se imprima en el espacio de espera.
  • Para cada lectura de línea, agregamos el espacio de espera al espacio de patrón para examinar la línea actual en relación con el estado existente del espacio de espera.
  • Ahora 5 cosas pueden suceder durante esta comparación:
    • a) La línea actual coincide en algún lugar de la línea de retención y falso: falso.
      • [ACCIÓN] Dado que se encuentra el mismo estado falso, no haga nada.
    • b) La línea actual coincide en algún lugar de la línea de retención y verdadero: verdadero.
      • [ACCIÓN] Dado que se encuentra el mismo estado verdadero, no haga nada.
    • c) La línea actual coincide en algún lugar de la línea de retención y verdadero: falso.
      • [ACCIÓN] Como ya existe un estado falso, no hagas nada.
    • d) La línea actual coincide en algún lugar de la línea de retención y falso: verdadero.
      • [ACCIÓN] Esto implica algo de trabajo, ya que necesitamos reemplazar la línea falsa en la misma posición exacta donde se encuentra el verdadero.
    • e) La línea actual NO coincide en ningún lugar de la línea de retención.
      • [ACCIÓN] Mueve la línea actual hasta el final.

Resultados

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

fuente
3

Para cada línea de entrada, almacene el valor del segundo campo en una matriz asociativa a(utilizando el primer campo como clave de la matriz) SOLAMENTE si aún no hemos almacenado el valor falsepara esa clave. Úselo ,tanto para el separador de campo de entrada como para el de salida. Imprima la matriz después de haber leído todas las líneas de entrada.

$ 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 diferencia significativa entre esto y la versión de DopeGhoti es que a esta versión no le importa en absoluto el valor de $2, solo le importa el valor, si lo hay, de a[$1].

cas
fuente
1

sortSolución de dos pasos

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

Primero sortpase los grupos de registros por campo 1con falseregistros que preceden truea cada bloque de registros que comparten un 1valor de campo común . El segundo sortpase está configurado para producir un registro para cada valor distinto dentro del campo, 1cortesía de -u. Como -uimplica una clasificación estable, el único registro así obtenido es el primer registro encontrado para cada valor distinto dentro del campo 1, que es un registro falseen el segundo campo debido al trabajo realizado por el primer sortpaso

iruvar
fuente