在Perl中从数组中删除值的最佳方法是什么?

时间:2008-10-06 13:29:56

标签: arrays perl

数组有很多数据,我需要删除两个元素。

以下是我正在使用的代码段

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;

14 个答案:

答案 0 :(得分:83)

如果您已经知道要删除的元素的索引,请使用splice。

如果您正在搜索,Grep会起作用。

如果你需要做很多这样的事情,如果你按照排序顺序保持你的数组,你将获得更好的性能,因为你可以进行二进制搜索以找到必要的索引。

如果在您的上下文中有意义,您可能需要考虑对已删除的记录使用“魔术值”,而不是删除它们,以节省数据移动 - 例如,将已删除的元素设置为undef。当然,这有其自身的问题(如果你需要知道“实时”元素的数量,你需要单独跟踪它等),但根据你的应用程序可能是值得的。

编辑实际上我现在再看看 - 不要使用上面的grep代码。找到要删除的元素的索引会更有效,然后使用splice删除它(你已经累积了所有不匹配的结果的代码..)

my $index = 0;
$index++ until $arr[$index] eq 'foo';
splice(@arr, $index, 1);

这将删除第一次出现。 删除所有匹配项非常相似,除非您希望一次性获取所有索引:

my @del_indexes = grep { $arr[$_] eq 'foo' } 0..$#arr;

剩下的作为读者的练习 - 请记住,当你拼接它时,数组会发生变化!

Edit2 John Siracusa正确地指出我的例子中有一个错误..修复,抱歉。

答案 1 :(得分:13)

splice将按索引删除数组元素。使用grep(如示例所示)进行搜索和删除。

答案 2 :(得分:8)

这是你要做的很多事情吗?如果是这样,您可能需要考虑不同的数据结构。 Grep每次都会搜索整个阵列,对于大型阵列来说,这可能会非常昂贵。如果速度是个问题,那么您可能需要考虑使用Hash。

在您的示例中,键是数字,值将是该数字的元素数。

答案 3 :(得分:3)

我认为您的解决方案是最简单,最易维护的。

帖子的其余部分记录了将元素测试转换为splice偏移的难度。因此,使其成为一个更完整的答案。

查看 回转 ,您必须通过有效(即一次通过)算法将列表项上的测试转换为索引。它根本不直观。

sub array_remove ( \@& ) { 
    my ( $arr_ref, $test_block ) = @_;
    my $sp_start  = 0;
    my $sp_len    = 0;
    for ( my $inx = 0; $inx <= $#$arr_ref; $inx++ ) {
        local $_ = $arr_ref->[$inx];
        next unless $test_block->( $_ );
        if ( $sp_len > 0 && $inx > $sp_start + $sp_len ) {
            splice( @$arr_ref, $sp_start, $sp_len );
            $inx    = $inx - $sp_len;
            $sp_len = 0;
        }
        $sp_start = $inx if ++$sp_len == 1;
    }
    splice( @$arr_ref, $sp_start, $sp_len ) if $sp_len > 0;
    return;
}

答案 4 :(得分:3)

您可以使用数组切片而不是拼接。 Grep返回你想保留的索引并使用切片:

my @arr = ...;
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indiciesToKeep];

答案 5 :(得分:2)

删除所有&#39;某些内容&#39;如果是数组。

基于SquareCog的答案:

my @arr = ('1','2','3','4','3','2', '3','4','3');
my @dix = grep { $arr[$_] eq '4' } 0..$#arr;
my $o = 0;
for (@dix) {
    splice(@arr, $_-$o, 1);
    $o++;
}
print join("\n", @arr);

每次我们从@arr删除索引时,下一个要删除的正确索引都是$_-current_loop_step

答案 6 :(得分:2)

我用:

delete $array[$index];

Perldoc delete

答案 7 :(得分:2)

您可以使用非捕获组和要删除的项目的管道分隔列表。


perl -le '@ar=(1 .. 20);@x=(8,10,3,17);$x=join("|",@x);@ar=grep{!/^(?:$x)$/o} @ar;print "@ar"'

答案 8 :(得分:2)

我找到的最好的是&#34; undef&#34;和&#34; grep&#34;:

foreach $index ( @list_of_indexes_to_be_skiped ) {
      undef($array[$index]);
}
@array = grep { defined($_) } @array;

这就是诀窍! 费德里科

答案 9 :(得分:1)

请务必确保已对grep和map解决方案进行了基准测试,首先搜索匹配元素的索引(要删除的元素),然后通过grep直接删除元素,而无需搜索索引。 我看来,Sam提出问题时提出的第一个解决方案已经是最快的。

    use Benchmark;
    my @A=qw(A B C A D E A F G H A I J K L A M N);
    my @M1; my @G; my @M2;
    my @Ashrunk;
    timethese( 1000000, {
      'map1' => sub {
          my $i=0;
          @M1 = map { $i++; $_ eq 'A' ? $i-1 : ();} @A;
      },
      'map2' => sub {
          my $i=0;
          @M2 = map { $A[$_] eq 'A' ? $_ : () ;} 0..$#A;
      },
      'grep' => sub {
          @G = grep { $A[$_] eq 'A' } 0..$#A;
      },
      'grem' => sub {
          @Ashrunk = grep { $_ ne 'A' } @A;
      },
    });

结果是:

Benchmark: timing 1000000 iterations of grem, grep, map1, map2...
  grem:  4 wallclock secs ( 3.37 usr +  0.00 sys =  3.37 CPU) @ 296823.98/s (n=1000000)
  grep:  3 wallclock secs ( 2.95 usr +  0.00 sys =  2.95 CPU) @ 339213.03/s (n=1000000)
  map1:  4 wallclock secs ( 4.01 usr +  0.00 sys =  4.01 CPU) @ 249438.76/s (n=1000000)
  map2:  2 wallclock secs ( 3.67 usr +  0.00 sys =  3.67 CPU) @ 272702.48/s (n=1000000)
M1 = 0 3 6 10 15
M2 = 0 3 6 10 15
G = 0 3 6 10 15
Ashrunk = B C D E F G H I J K L M N

如过去的时间所示,尝试实施删除是没有用的 使用grep或map定义的索引的函数。只需直接grep-remove。

在测试之前,我认为“ map1”将是最有效的……我应该更多地依靠基准。 ;-)

答案 10 :(得分:0)

如果您知道数组索引,可以delete()。 splice()和delete()之间的区别在于delete()不会重新编号数组的其余元素。

答案 11 :(得分:0)

我曾写过一个类似的代码,用于从字符串数组中删除不以SB.1开头的字符串

my @adoSymbols=('SB.1000','RT.10000','PC.10000');
##Remove items from an array from backward
for(my $i=$#adoSymbols;$i>=0;$i--) {  
    unless ($adoSymbols[$i] =~ m/^SB\.1/) {splice(@adoSymbols,$i,1);}
}

答案 12 :(得分:0)

您可以简单地执行以下操作:

my $input_Color = 'Green';
my @array = qw(Red Blue Green Yellow Black);
@array = grep {!/$input_Color/} @array;
print "@array";

答案 13 :(得分:0)

这也很好用:

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
for( my $i = 0; $i < scalar( @array ); $i++ )
{
    splice( @array, $i ), $i-- if( $array[$i] == $element_omitted );
}
say "@array"; # 1 2 3 4 6 4 9