Classificando exclusivamente de uma linha

3

Você pode, por favor, me dar uma sugestão de como posso classificar de uma linha ou linha? Eu tenho informações como esta:

Special   c1,c2,c5,c7,c1,c2   
Special2  C6

(é um caractere TAB entre Special e c1... ).

Eu quero a saída assim:

Special   c1,c2,c5,c7  
Special2  C6

Como posso conseguir isso?

    
por Masum Billah 24.04.2017 / 06:27

5 respostas

9

Usando esta resposta ,

perl -MList::MoreUtils=uniq -laF'\t' -ne '
    $F[1] = join(",", uniq(sort(split(",", $F[1])))); print join("\t", @F)'

Isso depende de um pacote externo List :: MoreUtils . Se você não quiser instalar uma dependência externa, reimplementando a função uniq é apenas mais algumas linhas do Perl . (Embora pareça ter instalado como parte do sistema base no macOS).

    
por 24.04.2017 / 07:26
7
perl -F'\t|,' -lane 'my %h; print shift @F, "\t", join ",", sort grep !$h{$_}++, @F' dataf

Explicação

  • -F'\t|,' = > dividirá cada campo de registro na matriz @F em TAB ou comma caracteres.
  • -l definirá também o RS para newline e ORS para newline .
  • -a irá autosplitar cada registro em palavras com base no FS escolhido por -F .
  • -n configurará um registro implícito em loop de leitura na entrada AND imprimirá as coisas somente quando solicitado.
  • -e é o código Perl a ser executado em cada registro da entrada com base em RS choen por -l acima.
  • O primeiro elemento será dado por shift e os elementos restantes serão uniquified por meio do armazenamento como chaves de um hash, %h , que será regenerado toda vez que um registro for lido. Os elementos exclusivos são depois ordenou e juntou-se a uma vírgula e imprimiu.
por 24.04.2017 / 10:28
5

Testado com o OpenBSD awk , GNU awk e mawk :

awk -F ',| +' '{ for (i = 2; i <= NF; ++i) { print $1, $i } }' data.in |
sort -u |
awk '{ f[$1] = (f[$1] ? f[$1] "," : "") $2 } END { for (k in f) { print k, f[k] } }'

O primeiro awk expande os dados fornecidos para

Special c1
Special c2
Special c5
Special c7
Special c1
Special c2
Special2 C6

Ele usa tanto vírgulas como vários espaços como delimitador de campo e, para cada registro (linha) de entrada, ele imprime o primeiro campo seguido por cada um dos outros campos, por sua vez, em linhas separadas. Isso pressupõe que não há outros espaços ou vírgulas nas linhas além de onde eles serão interpretados corretamente como delimitadores.

O sort no meio classifica-o em

Special2 C6
Special c1
Special c2
Special c5
Special c7

Ele faz uma classificação usando a linha completa como a chave de classificação e descarta qualquer linha duplicada.

O último awk recombina os dados em

Special c1,c2,c5,c7
Special2 C6

Ele faz isso usando o primeiro campo como uma chave em uma matriz associativa e armazena a concatenação separada por vírgula dos dados correspondentes no segundo campo como o valor. No final, todos os dados coletados são impressos.

    
por 24.04.2017 / 09:37
5
Abordagem

gawk (GNU awk):

 awk '{if($2~/.*,.*/){l=split($2,a,","); asort(a); $2=a[1]; b[a[1]]++;
      for(i=2;i<=l;i++) $2=(!b[a[i]]++)? $2","a[i] : $2 }}1' file

A saída:

Special c1,c2,c5,c7
Special2  C6

if($2~/.*,.*/) - se tivermos vários itens separados por vírgulas no segundo campo

l=split($2,a,",") - dividindo a string em partes separadas por , .
l é atribuída com o número de partes

asort(a) - classifica uma matriz de substrings

$2=(!b[a[i]]++)? $2","a[i] : $2 - considerando itens exclusivos

    
por 24.04.2017 / 08:58
2

Outra maneira em uma linha:

while read line; do echo "$line" | awk '{print $1}' | tr '\n' ' ';  echo "$line" | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',' | sed -e 's/.$//g'; echo; done < file_to_sort

Ele pega a primeira coluna de cada linha ( echo $line | awk '{print $1}' | tr '\n' ' '; ) e classifica os valores da segunda coluna separados por ',' depois de convertê-la em uma única coluna para aplicar sort e convertê-la de volta em uma única linha com a formatação original ( echo $line | awk '{print $2}' | tr ',' '\n' | sort -u | tr '\n' ',' ).

Fazendo a divisão de linha conforme sugerido por @tripleee:

while IFS=$'\t' read first second; do printf "%s\t%s\n" "$first" "$(tr ',' '\n' <<<"$second" | sort | tr '\n' ',' | sed -e 's/.$//g';)"; done < file_to_sort
    
por 24.04.2017 / 09:08