web-dev-qa-db-ja.com

末尾にSがない重複が同じリストにある場合、文字Sで終わる単語を削除するにはどうすればよいですか?

単語のリストがたくさんあります。単語の多くは、末尾にsが付いているため、異なるだけです。リスト上の単語がリスト上の別の単語とまったく同じである場合、単語の1つが文字sで終わることを除いて、sで終わる重複する単語を削除したいと思います。また、単語の現在の位置を維持できるように、リストを並べ替えることなくこれを実現したいと思います。

入力例:

frog
dogs
cats
cat
dog
frogs
catfish
octopus

出力例:

frog
cat
dog
catfish
octopus
2
J363

Awkを使用してファイルを2回読み取ります。すべての変数を配列に保存し、最後にsを付けます。 2回目の実行で各行の配列を確認し、その行が配列にない場合は印刷します。

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

少し少ないメモリを使用するには、

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

これはいくつかの方法で行うことができます。たとえば、最も簡単な方法は、データを並べ替えて隣接する行を比較することです。

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

与えられた入力

frog
dogs
cats
catfish
cat
dog
frogs

出力

cat
catfish
dog
frog

並べ替えなし:

#!/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

出力:

frog
catfish
cat
dog
3
Thomas Dickey

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

リストはstdinから読み取られます。テスト実行は次のとおりです。

$ cat file1
frog
dogs
cats
cat
dog
frogs
catfish
$ ./remove-s.sh < file1 
frog
cat
dog
catfish
3
adonis

Grepの-f(ファイルからパターンを取得)オプションを過度に使用すると、次のようになります。

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
1
JigglyNaga
#! /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";
}

出力:

frog
cat
dog
catfish
octopus
1
cas

これは、awkを使用した単純化されたソリューションであり、語順は保持されません。

    {
        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;
        }
    }

語順を維持することが不可欠な場合は、すべての行をメモリに保持し、ファイル全体が読み取られた後にリストを処理する必要があります。

{
    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];
        }
    }
}
1
Michael Vehrs