Como removo as palavras que terminam na letra S se as duplicatas sem um S no final estiverem na mesma lista?

3

Eu tenho uma lista grande de palavras. Muitas das palavras são diferentes porque têm a letra s no final. Se uma palavra na lista for exatamente igual a outra palavra na lista, exceto que uma das palavras termina com a letra s, eu gostaria de remover a palavra duplicada que termina em s. Eu também gostaria de conseguir isso sem ter que classificar a lista para que eu possa manter a posição atual das palavras.

exemplo de entrada:

frog
dogs
cats
cat
dog
frogs
catfish
octopus
Exemplo de saída

:

frog
cat
dog
catfish
octopus
    
por J363 31.05.2016 / 10:45

6 respostas

4

Usando o awk e lendo o arquivo duas vezes. Salve todas as variáveis na matriz com s no final. Verifique a matriz em cada linha na segunda execução e imprima se a linha não estiver na matriz.

awk 'FNR==NR{a[$0 "s"]++;next}!($0 in a)' file.txt file.txt

Para usar um pouco menos de memória, você também pode fazer

awk 'FNR==NR{!/s$/ && a[$0 "s"]++;next}!($0 in a)' file.txt file.txt
    
por 31.05.2016 / 11:36
3

Você pode fazer isso de várias maneiras, por exemplo, a maneira mais simples seria classificar os dados e comparar as linhas adjacentes:

sort foo |awk '{ if ( plural[$1] == "" ) print; plural[$1 "s"] = 1; }'

Dado entrada

frog
dogs
cats
catfish
cat
dog
frogs

saída

cat
catfish
dog
frog

Sem classificação:

#!/bin/sh
awk 'BEGIN { count=0; }
{
        words[count++] = $1;
        plurals[$1 "s"] = $1;
}
END {
        for ( n = 0; n < count; ++n) {
                if ( plurals[words[n]] == "")
                        print words[n];
        }
}
' <foo

Saída:

frog
catfish
cat
dog
    
por 31.05.2016 / 11:05
3

Usando um script bash:

#!/bin/bash

readarray -t mylist

# compare each item on the list with a new list created by appending 's'
# to each item of the original list

for i in "${mylist[@]}"; do
  for j in "${mylist[@]/%/s}"; do
    [[ "$i" == "$j" ]] && continue 2
  done
  echo "$i"
done

A lista é lida de stdin. Aqui está um teste:

$ cat file1
frog
dogs
cats
cat
dog
frogs
catfish
$ ./remove-s.sh < file1 
frog
cat
dog
catfish
    
por 31.05.2016 / 12:20
1

Esta é uma solução simplificada usando awk , que não preserva a ordem das palavras:

    {
        len = length($1);
        prefix = $1;
        if (substr($1, len) == "s") {
            prefix = substr($1, 1, len - 1);
        }
        if (prefix in data) {
            next;
        } else {
            print prefix;
            data[prefix] = 1;
        }
    }

Se for essencial preservar a ordem das palavras, você terá que manter todas as linhas na memória e processar a lista depois que o arquivo inteiro tiver sido lido.

{
    line[FNR] = $0;
    len = length($1);
    if (substr($1, len) == "s") {
        prefix = substr($1, 1, len - 1);
        if (prefix in data) {
            line[FNR] = "";
            next;
        } else {
            data[prefix] = FNR;
        }
    } else {
        num = data[$1];
        if (num) {
            line[num] = "";
        } else {
            data[$1] = FNR;
        }
    }
}

END {
    for (i = 1; i <= FNR; i++) {
        if (line[i]) {
            print line[i];
        }
    }
}
    
por 31.05.2016 / 11:01
1

Com o uso excessivo da opção -f (obter padrões do arquivo) do grep:

grep 's$' input       | # output: all lines ending with s 
  sed -e 's/s$//'     | # those same entries, minus the s
  grep -F -x -f input | # the entries whose plurals appear
  sed -e 's/$/s/'     | # the plurals to remove
  grep -F -x -v -f - input
    
por 01.06.2016 / 01:14
1

#! /usr/bin/perl

use strict;

my %words = ();
my $index = 1;  # keep track of the order that words were read in

while (<>) {
  chomp;
  $words{$_} = $index++ 
}

# sort %words hash by value to print words in the same order
# that they were seen.
foreach (sort { $words{$a} <=> $words{$b} } keys %words) {
  my $word = $_;
  $word =~ s/s$//;
  next if ( ($word ne $_) && (defined $words{$word}) );
  print "$_\n";
}

Saída:

frog
cat
dog
catfish
octopus
    
por 01.06.2016 / 03:02