从数组中删除具有子字符串的元素,子字符串本身就是数组的元素

时间:2013-01-17 15:25:20

标签: arrays perl

在Perl中,我想从数组中删除所有元素,其中同一数组的另一个元素是所述元素的非空子字符串。

说我有阵列

@itemlist = ("abcde", "ab", "khi", "jklm");

在这种情况下,我希望删除元素"abcde",因为"ab""abcde"的子字符串。

我可以制作一个数组的副本(可能是一个哈希?),迭代它,尝试索引原始数组的每个元素并删除它,但是必须有一个更优雅的方式,不是吗? / p>

感谢您的帮助!

为了清晰起见而进行了编辑。

6 个答案:

答案 0 :(得分:3)

你可以从所有项目中构造一个正则表达式并抛弃任何匹配的东西:

$alternation = join('|', map(quotemeta, @itemlist));
@itemlist = grep !/($alternation).|.($alternation)/, @itemlist;

().|.()只会确保项目与自身不匹配。

答案 1 :(得分:1)

好吧,我不会称之为优雅,但这里是:

#!usr/bin/perl
use strict;
use warnings;

my @itemlist = ("abcde", "ab", "khi", "jklm");

@itemlist = grep { 
    @itemlist ~~ sub {$_ !~ /(?:.\Q$_[0]\E|\Q$_[0]\E.)/} 
} @itemlist;

print "@itemlist";

它依赖于smart match的一个相当模糊的行为:如果左参数是一个数组而右参数是一个sub,它会为每个元素调用sub,并且只有当sub返回时,最终结果才为真每个元素都为true。

说明:对于数组的每个元素,它检查没有其他元素是该元素的子字符串(至少需要一个附加字符,以便元素本身不匹配)。

注意: wdebeaum的答案可能是我在现实世界中更喜欢的答案。尽管如此,聪明的比赛可以做一些奇怪的事情。

答案 2 :(得分:0)

wdebeaum的答案是使用的解决方案,而不是下面的解决方案,但我通过这样做了解了一些东西,也许其他人也会这样做。在我写完之后,我决定在几千个元素的列表上进行测试。

b.pl:

#!/usr/bin/perl

use strict;
use warnings;

my @itemlist = <>;
for(@itemlist) { chomp; }
my $regex;

if(defined $ENV{wdebeaum}) {
    # wdebeaum's solution
    my $alternation = join('|', map(quotemeta, @itemlist));
    $regex = qr/(?:$alternation).|.(?:$alternation)/;
} else {
    # my solution
    $regex = join "|", map {qq{(?:\Q$_\E.)|(?:.\Q$_\E)}} @itemlist;
}

my @result = grep !/$regex/, @itemlist;
print scalar @itemlist, "\t", scalar @result, "\n";

我生成了一个包含5000个随机单词的列表。

sort -R /usr/share/dict/american-english|head -5000 > some-words

对于小型列表,两种解决方案似乎都很好。

$ time head -200 some-words | wdebeaum=1 ./b.pl
200 198

real    0m0.012s
user    0m0.004s
sys     0m0.004s

$ time head -200 some-words | ./b.pl
200 198

real    0m0.068s
user    0m0.060s
sys     0m0.004s

但是对于较大的名单,wdebeaum显然更好。

$ time cat some-words | wdebeaum=1 ./b.pl 
5000    1947

real    0m0.068s
user    0m0.064s
sys     0m0.000s

$ time cat some-words | ./b.pl 
5000    1947

real    0m8.305s
user    0m8.277s
sys     0m0.012s

我认为差异的原因在于,即使两个正则表达式具有相同数量的可能路径,我的正则表达式也有更多必须尝试的路径,因为它具有相同数量的. s作为路径,而wdebebaum只有两个。

答案 3 :(得分:0)

您可以使用哈希来计算所有单词的子字符串。列表中任何数字大于1的单词都是另一个单词的子字符串。在此示例中,子串的最小长度为2:

use strict;
use warnings;
use feature 'say';

my @list = qw(abcde ab foo foobar de oba cd xs);

my %count;

for my $word (@list) {
    my $len = length $word;
    $count{$word}++;
    for my $start (0 .. $len - 2) {
        for my $long (2 .. $len - 2) {
            my $sub = substr($word, $start, $long);
            $count{$sub}++;
        }
    }
}
say for grep $count{$_} == 1, @list;

<强>输出:

abcde
foobar
xs

答案 4 :(得分:0)

以下内容将从数组中删除子字符串。

#!/usr/bin/perl
use strict;
use warnings;

my @ar=("asl","pwe","jsl","nxu","sl","baks","ak");
foreach my $i (@ar){
  my $p = grep /$i/, @ar;
  if ( $p == 1 ){
    print "$i" , "\n";
  }
} 

答案 5 :(得分:0)

我遇到了相反的问题:从列表中删除其他字符串的子字符串。这是我不太优雅的解决方案。

sub remove_substrings_from_list {
    my @list = @_;
    my @vals_without_superstrings;

    my %hash_of_others;
    for ( 0 .. $#list ) {
        my $a = shift @list;
        $hash_of_others{$a} = [ @list ];
        push @list, $a;
    }
    foreach my $k ( keys %hash_of_others ) {
        push @vals_without_superstrings, $k unless grep { index( $_, $k ) != -1 } @{ $hash_of_others{$k} };
    }
    return @vals_without_superstrings;
}