remova 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
Hani Gotc
fonte
2
Então .. só mantenha truese for a primeira instância da primeira coluna?
DopeGhoti 20/07
1
@RomanPerekhrest Provavelmente porque é uma entrada uniq e é impresso "como é"
George Vasiliou
@RomanPerekhrest porque DD, true não é uma duplicata, não temos outra linha com DD, false.
Hani Gotc
AA,true AA,false AA,false AA,falseQual saída deve ser neste caso? Entendo que essa linha deve ser removida apenas se estiver duplicada e contiver trueao mesmo tempo. Todas as falselinhas devem permanecer intocadas em qualquer caso. Ou seja, neste caso, apenas AA, trueserão removidos. Mas todas as respostas deixam apenas uma linha - AA,false. Apenas interessante :)
MiniMax 21/07

Respostas:

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 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]
   }
}
DopeGhoti
fonte
@DopeGhoti bem explicado! Você tem o meu +1 nisso.
Valentin Bajrami
14

Versão simples:

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

"false" classifica em ordem alfabética antes de "true" e o comando Awk aqui apenas mantém a primeira linha apenas para cada valor distinto do primeiro campo.

Se você quiser manter "true" em vez de "false", classifique-o inversamente, passe-o para o mesmo comando do Awk e, novamente, classifique-o novamente depois.

Curinga
fonte
1
Além disso, se -uopção está disponível,sort input.txt | sort -t, -u -k1,1
Sundeep
2
@ Sundeep por que usar duas sortchamadas? Por que não apenas sort -ut, -k1,1 input.txt ?
terdon
2
@terdon porque -ureterá a primeira linha encontrada do arquivo de entrada entre duplicatas ... para um determinado caso, a entrada deve ser classificada antes que -upossa ser aplicada ... por ex: AA,trueserá impressa em vez de, AA,falsepois aparece primeiro na amostra especificada .. mesma razão pela qual awk -F, '!a[$1]++'por si só não vai 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

Estruturas de dados:

  • Hash %hcujas chaves são os primeiros campos (AAA, BBB, CCC etc.) e os valores correspondentes são números que indicam a ordem em que as chaves foram encontradas. Assim, por exemplo, chave AAA => 0, chave BBB => 1, chave CCC => 2.
  • Matriz @hcujos elementos são linhas contidas na ordem de impressão. Portanto, se true e false forem encontrados nos dados, o valor false entrará na matriz. OTW, se houver um tipo de dados, isso estaria presente.

Outra maneira é usar o 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, o código equivalente POSIX para o código GNU-sed acima está listado abaixo:

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

Explicação

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

Resultados

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

fonte
3

Para cada linha de entrada, armazene o valor do segundo campo na matriz associativa a(usando o primeiro campo como chave da matriz) SOMENTE se ainda não tivermos armazenado o valor falsedessa chave. Use ,para o separador de campos de entrada e saída. Imprima a matriz depois de ler 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, apenas se importa com o valor, se houver a[$1].

cas
fonte
1

sortSolução de duas passagens

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

Primeiramente sortpasse clusters registros por campo 1com falseregistros anteriores truepara cada bloco de registros que compartilham um 1valor de campo comum . A segunda sortpassagem é configurada para gerar um registro para cada valor distinto no campo, 1cortesia de -u. Como -uimplica classificação estável, o único registro assim gerado é o primeiro registro encontrado para cada valor distinto no campo 1- que é um registro falseno segundo campo devido ao trabalho realizado pela primeira sortpassagem

iruvar
fonte