What is the best way to delete a value from an array in Perl?

前端 未结 14 2373
忘掉有多难
忘掉有多难 2020-12-12 20:17

The array has lots of data and I need to delete two elements.

Below is the code snippet I am using,

my @array = (1,2,3,4,5,5,6,5,4,9);
my $element_o         


        
相关标签:
14条回答
  • 2020-12-12 20:53

    Use splice if you already know the index of the element you want to delete.

    Grep works if you are searching.

    If you need to do a lot of these, you will get much better performance if you keep your array in sorted order, since you can then do binary search to find the necessary index.

    If it makes sense in your context, you may want to consider using a "magic value" for deleted records, rather then deleting them, to save on data movement -- set deleted elements to undef, for example. Naturally, this has its own issues (if you need to know the number of "live" elements, you need to keep track of it separately, etc), but may be worth the trouble depending on your application.

    Edit Actually now that I take a second look -- don't use the grep code above. It would be more efficient to find the index of the element you want to delete, then use splice to delete it (the code you have accumulates all the non-matching results..)

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

    That will delete the first occurrence. Deleting all occurrences is very similar, except you will want to get all indexes in one pass:

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

    The rest is left as an excercise for the reader -- remember that the array changes as you splice it!

    Edit2 John Siracusa correctly pointed out I had a bug in my example.. fixed, sorry about that.

    0 讨论(0)
  • 2020-12-12 20:53

    A similar code I once wrote to remove strings not starting with SB.1 from an array of strings

    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 讨论(0)
  • 2020-12-12 21:00

    You could use array slicing instead of splicing. Grep to return the indices you want keep and use slicing:

    my @arr = ...;
    my @indicesToKeep = grep { $arr[$_] ne 'foo' } 0..$#arr;
    @arr = @arr[@indiciesToKeep];
    
    0 讨论(0)
  • 2020-12-12 21:00

    You can use the non-capturing group and a pipe delim list of items to remove.

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

    I think your solution is the simplest and most maintainable.

    The rest of the post documents the difficulty of turning tests on elements into splice offsets. Thus, making it a more complete answer.

    Look at the gyrations you have to go through to have an efficient (i.e. one-pass) algorithm to turn tests on list items into indexes. And it's not that intuitive at all.

    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;
    }
    
    0 讨论(0)
  • 2020-12-12 21:02

    You can simply do this:

    my $input_Color = 'Green';
    my @array = qw(Red Blue Green Yellow Black);
    @array = grep {!/$input_Color/} @array;
    print "@array";
    
    0 讨论(0)
提交回复
热议问题