remove as duplicatas com base no valor de outra coluna

9

Eu tenho o seguinte arquivo:

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

Estou tentando procurar duplicatas e remover a linha que tem o valor da coluna igual a true .

como saída, deve ser:

AA,false
BB,false
CC,false
DD,true
    
por Hani Gotc 20.07.2017 / 23:17

5 respostas

8
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 o script verticalmente para explicação:

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]
   }
}
    
por 20.07.2017 / 23:22
14

Versão simples:

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

"false" classifica alfabeticamente antes de "true", e o comando Awk aqui apenas mantém a primeira linha apenas para cada primeiro valor de campo distinto.

Se você quiser manter "true" em vez de "false", faça um ordenamento inverso, passe-o para o mesmo comando Awk e inverta-o para ordená-lo novamente.

    
por 20.07.2017 / 23:35
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

Estruturas de dados:

  • Hash %h cujas chaves são os primeiros campos (AAA, BBB, CCC, etc.) e os valores correspondentes são números informando a ordem em que as chaves foram encontradas. Assim, por exemplo, chave AAA = > 0, chave BBB = > 1, chave CCC = > 2.
  • Matriz @h cujos elementos são linhas contidas na ordem de impressão. Então, se tanto true quanto false forem encontrados nos dados, então o valor false irá para o array. OTW, se houver um tipo de dado, isso estaria presente.

Outra maneira é usar o GNU sed:

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

FWIW, o código equivalente do POSIX para o código acima do GNU-sed é listado abaixo:

sed -e '
   G

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

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

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

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

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

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

Explicação

  • Nesse método, armazenamos o resultado a ser finalmente impresso no espaço de armazenamento.
  • Para cada linha lida, anexamos o espaço de espera ao espaço de padrão para exame da linha atual vis-à-vis o estado existente do espaço de espera.
  • Agora, 5 coisas podem acontecer durante essa comparação:
    • a) A linha atual corresponde a algum lugar na linha de espera & false: false.
      • [ACTION] Como o mesmo estado falso é encontrado, não faça nada.
    • b) A linha atual corresponde a algum lugar na linha de espera & verdade verdade.
      • [ACTION] Como o mesmo estado verdadeiro é encontrado, não faça nada.
    • c) A linha atual corresponde a algum lugar na linha de espera & verdadeiro falso.
      • [ACTION] Como já existe um estado falso, não faça nada.
    • d) A linha atual corresponde a algum lugar na linha de espera & falso verdadeiro.
      • [ACTION] Isso envolve algum trabalho, na medida em que precisamos substituir a linha falsa exatamente na mesma posição em que o verdadeiro está localizado.
    • e) A linha atual NÃO corresponde em nenhum lugar da linha de espera.
      • [ACTION] Mova a linha atual até o final.

Resultados

AA,false
BB,false
CC,false
DD,true
    
por 21.07.2017 / 16:39
3

Para cada linha de entrada, armazene o valor do segundo campo na matriz associativa a (usando o primeiro campo como a chave da matriz) SOMENTE se ainda não tivermos armazenado o valor false para essa chave. Use , para o separador de campo de entrada e saída. Imprima o array depois de lermos todas as linhas 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

A diferença significativa entre esta e a versão do DopeGhoti é que esta versão não se importa com o valor de $2 , só se preocupa com o valor, se houver, de a[$1] .

    
por 21.07.2017 / 04:13
1

Duas passagens sort solution

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

Primeiro sort passar registros de clusters pelo campo 1 com false registros anteriores a true para cada bloco de registros que compartilha um campo comum 1 value. O segundo sort pass está configurado para gerar um registro para cada valor distinto no campo 1 cortesia de -u . Como -u implica em ordenação estável, o registro assim obtido é o primeiro registro encontrado para cada valor distinto no campo 1 - que é um registro com false no segundo campo devido ao trabalho realizado pelo primeiro sort pass

    
por 23.07.2017 / 02:51