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

问题描述 投票:0回答:15

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

下面是我正在使用的代码片段,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_omitted = 5;
@array = grep { $_ != $element_omitted } @array;
arrays perl
15个回答
96
投票

如果您已经知道要删除的元素的索引,请使用 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 正确地指出我的示例中有一个错误..已修复,对此感到抱歉。


16
投票

splice 将按索引删除数组元素。 使用 grep(如您的示例中所示)来搜索和删除。


12
投票

您可以简单地这样做:

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

8
投票

这是您会经常做的事情吗? 如果是这样,您可能需要考虑不同的数据结构。 Grep 每次都会搜索整个数组,对于大型数组可能会非常昂贵。 如果速度是一个问题,那么您可能需要考虑使用哈希来代替。

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


5
投票

如果你改变

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

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

这可以通过首先从数组后面删除元素来避免数组重新编号问题。 将 splice() 放入 foreach 循环中会清理 @arr。相对简单易读...

foreach $item (@del_indexes) {
   splice (@arr,$item,1);
}

5
投票

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

my @arr = ...;
# run through each item.
my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
@arr = @arr[@indicesToKeep];

4
投票

我发现最好的是“undef”和“grep”的组合:

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

这就是窍门! 费德里科


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



2
投票

基于 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
    


2
投票

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



1
投票

delete $array[$index];

Perldoc 

删除


1
投票

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”将是最有效的......我想我应该更频繁地依赖 Benchmark。 ;-)


0
投票
delete()

它。 splice() 和 delete() 之间的区别在于,delete() 不会对数组的剩余元素重新编号。


0
投票

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



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

	
© www.soinside.com 2019 - 2024. All rights reserved.