【问题标题】:What is the best way to delete a value from an array in Perl?在 Perl 中从数组中删除值的最佳方法是什么?
【发布时间】:2010-09-15 12:21:37
【问题描述】:

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

下面是我正在使用的代码sn-p,

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

【问题讨论】:

  • 这会删除三个元素。
  • needed top remove all non-file item form directory lists and "array = grep { -f $_ } array" 对我来说就像一个魅力:)

标签: arrays perl


【解决方案1】:

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

如果您正在搜索,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 正确地指出我的示例中有一个错误。已修复,对此感到抱歉。

【讨论】:

  • 如果找不到字符串,循环就会卡住,我的$index = 0;也是这样我的 $count = 标量 @arr; $index++ 直到 $arr[$index] eq 'foo' 或 $index==$count;拼接(@arr, $index, 1);
  • my ($index) = grep { $arr[$_] eq 'foo' } 0..$#arr; if (defined $index) {splice(@arr, $index, 1); } - 第一场比赛
【解决方案2】:

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

【讨论】:

  • 谢谢斯波尔森。我不知道我必须删除的索引,所以我不得不求助于 grep。
【解决方案3】:

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

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

【讨论】:

    【解决方案4】:

    如果你改变了

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

      【讨论】:

      • 我特别喜欢这种方法的逻辑性和优雅性。
      • 是的,您甚至可以将它写成一个单行字,例如:@arr = @arr[grep ...],我特别喜欢。我不确定它的效率如何,但我会开始使用它,因为它不会比其他解决方案差。
      【解决方案6】:

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

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

      这就是诀窍! 费德里科

      【讨论】:

      • undef 将元素值设置为 null。总元素(大小)仍然相同。
      • @BoontaweeHome,最后的grep 然后删除它们。
      • 如果您已经知道索引,只需使用splice
      【解决方案7】:

      你可以这样做:

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

      【讨论】:

        【解决方案8】:

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

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

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

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

        【讨论】:

        • 一个简单的“grep”将比这更容易理解和更有效。
        • 有人删除了我的评论,说你显然没有看文字。
        【解决方案9】:

        我用:

        delete $array[$index];
        

        Perldoc delete.

        【讨论】:

        • delete 数组值可能已被弃用(请参阅您的文档)
        • 这只是删除存储在该数组索引处的值。至少在我的 perl 版本中,(5.14)
        • 这并没有真正删除你的想法。它只删除该值,使其成为undef。此外,来自 ringø 链接的文档:“警告:强烈建议不要对数组值调用 delete。删除或检查 Perl 数组元素的存在的概念在概念上并不连贯,并且可能导致令人惊讶的行为。” (文档中的前一段包含所有血淋淋的细节)。
        【解决方案10】:

        删除所有出现的 'something' if 数组。

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

        【讨论】:

          【解决方案11】:

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

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

          【讨论】:

            【解决方案12】:

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

                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
            

            从经过的时间可以看出,尝试实现remove是没有用的 函数使用 grep 或 map 定义的索引。直接grep-remove即可。

            在测试之前,我认为“map1”将是最有效的......我猜我应该更频繁地依赖 Benchmark。 ;-)

            【讨论】:

              【解决方案13】:

              如果你知道数组索引,你可以delete()它。 splice() 和 delete() 的区别在于 delete() 不会对数组的剩余元素重新编号。

              【讨论】:

              • 我实际上的意思是重新编号,根据 Perldoc,splice() 确实如此。
              【解决方案14】:

              我曾经写过一个类似的代码,用于从字符串数组中删除不以 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);}
              }
              

              【讨论】:

                【解决方案15】:

                这也很好用:

                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
                

                【讨论】:

                  猜你喜欢
                  • 1970-01-01
                  • 1970-01-01
                  • 2010-12-17
                  • 2022-08-12
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  • 2011-04-09
                  相关资源
                  最近更新 更多