2

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.

7
  • Do you really insist on using splice? Any particular reasons for that?
    – zdim
    Commented Jul 8 at 4:58
  • no particular reason, just like splice does not leave any undef at the deleted position. Commented Jul 8 at 5:19
  • 1
    my %h = map {$_=>1} qw(sam yam lam); @arr = grep { !defined $h{$_} } @arr; ?
    – jhnc
    Commented Jul 8 at 5:29
  • What about these elements makes them "random"? With this question you will only get answers to remove predefined elements, not random elements.
    – TLP
    Commented Jul 8 at 13:53
  • 1
    @A.G.Progm.Enthusiast Ah, you mean random indexes, not random elements. I would go for doing the selection when you gather the array elements, not afterwards.
    – TLP
    Commented Jul 8 at 17:43

2 Answers 2

3

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.

0
2

This might not be the best way to do it, but you can remove them without recomputing indices if you remove the highest index first and work your way back to the lowest index. That way, no elements that you want to remove change position. To do this, you'd start with all the indices already known, then go through a separate step to remove them.

3
  • This is the exact solution of my requirement. Commented Jul 8 at 16:43
  • 2
    @A.G.Progm.Enthusiast It is not. He means that if you need to remove index number 2, 3 and 7, you remove them in the order 7,3,2. That way the removal of an index doesn't affect the other indexes.
    – TLP
    Commented Jul 8 at 17:47
  • @TLP correct, but the algorithm proposed (highest index to lowest) does the job. Commented Jul 9 at 4:00

Not the answer you're looking for? Browse other questions tagged or ask your own question.