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

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 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.


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

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].


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

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.