arraysperlsplice

How do I remove random elements in one-go from an array using splice in Perl?


I have an array as below :

my @arr = qw (ram sam ham dam yam fam lam cam) 

I want to remove sam , yam and lam from the above array using splice. I am using below code to do it :

# first getting the index of sam 
(my  $ind ) = grep {$arr[$_] eq 'sam'} 0 .. $#arr;
splice(@arr, $ind, 1); 

My question is since the array elements have changed their position, I need to again fetch the index of yam and lam and do a splice on the array.

Is there any other way so that in one-go I will remove the random elements from the array using splice. I can get the index of all of them in once and delete them from the array but those positions will hold undef value which I do not want.


Solution

  • If you know the values of elements to remove (sam etc in this case), and not indices, can do

    my $re = join '|', map { q(^) . quotemeta . q(\z) } qw(sam yam lam);
    
    my @filtered_ary = grep { not /$re/ } @ary;
    

    or, really

    my $re = join '|', map { quotemeta } qw(sam yam lam); 
    $re = qr/^(?:$re)\z/; 
    
    my @filtered_ary = grep { not /$re/ } @ary;
    

    Can also overwrite the array

    @ary = grep { ... } @ary
    

    See quotemeta which escapes "all ASCII non-"word" characters" so that characters with a special meaning in a regex can be used as literal characters, and qr, building a regex. The use of qr is most of the time not necessary but I consider it good practice.

    No need to first find the index in order to use splice (and which you'd have to use repeatedly for all non-contiguous indices).


    Or, as in jhnc comment

    my %remove = map { $_ => 1 } qw(sam yam lam); 
    
    @ary = grep { not $remove{$_} } @ary;
    

    This is quicker than the solution above as it only does a lookup instead of a regex, while their overhead is fairly similar.