Beefy Boxes and Bandwidth Generously Provided by pair Networks
Your skill will accomplish
what the force of many cannot
 
PerlMonks  

How can I delete an element in a foreach cycle?

by saintex (Scribe)
on Feb 26, 2010 at 23:07 UTC ( [id://825596]=perlquestion: print w/replies, xml ) Need Help??

saintex has asked for the wisdom of the Perl Monks concerning the following question:

hello I would like to delete an element, if a condition is verified in a foreach statement. Something like this:
for (@ARGV) { # delete($_) if $_ eq 'hello'; }
The code is wrong for sure, because $_ is not the array element, but just a reference to his scalar... but how can I do that?

Replies are listed 'Best First'.
Re: How can I delete an element in a foreach cycle?
by kennethk (Abbot) on Feb 26, 2010 at 23:12 UTC
    Is there a particular need to do this in a foreach loop and in place? For example, this would seem to me to be a textbook example of when to use grep:

    @array = qw(hi hello howdy); @array = grep {!/^hello$/} @array; print join "\n", @array;

    If you need to do this in the context of a loop, perhaps it would make sense to combine loop control with undef and filtering after the loop:

    @array = qw(hi hello howdy); for (@array) { if ($_ eq 'hello') { undef $_; next; } # Process list elements } @array = grep {defined} @array; print join "\n", @array;
      That would filter out any elements that were undefined to begin with. Another solution:
      my @keep; for (my $i = 0; $i < @array; $i++) { my $_ = $array[$i]; push @keep, $i unless $_ eq 'hello'; } @array = @array[@keep];
      Or:
      for (my $i = @array; $i--;) { splice @array, $i, 1 if $array[$i] eq 'hello'; }
      But splice can be expensive; don't use that when deleting lots of things from a huge array.

        It's never a good idea to mess with the JavaFan, but would either of those approaches be faster or more straightforward than, or in any way preferable to something like (assuming the array may contain undefined elements):

        >perl -wMstrict -le "my @array = (qw(hi hiya hello hello), undef, qw(lo hello)); @array = grep !defined || $_ ne 'hello', @array; print defined($_) ? qq{'$_'} : 'UNDEF' for @array; " 'hi' 'hiya' UNDEF 'lo'
Re: How can I delete an element in a foreach cycle?
by CountZero (Bishop) on Feb 27, 2010 at 08:49 UTC
    Generally it is considered a bad idea to delete or add elements to an array your are looping over. Unless you are very careful, you might either skip elements or fall into an infinite loop.

    That being said, if you do not mind adding a temporary variable, this is another solution:

    use strict; use warnings; my @array = qw /aap noot mies wim zus jet teun vuur gijs lam kees bok +weide does hok duif schapen/; my @temp_array; for (@array) { push @temp_array, $_ unless /a/; } @array = @temp_array; { local $, = '|'; print @array; }
    Or at the cost cost of some added complexity, using slices and grep and map (how perlish can you get?):
    use strict; use warnings; my @array = qw /aap noot mies wim zus jet teun vuur gijs lam kees bok +weide does hok duif schapen/; my $counter = -1; @array = @array[grep {$_} map {$counter++; /a/ ? undef : $counter;} @a +rray]; { local $, = '|'; print @array; }
    the output of both is:
    noot|mies|wim|zus|jet|teun|vuur|gijs|kees|bok|weide|does|hok|duif
    Update: added another solution.

    CountZero

    A program should be light and agile, its subroutines connected like a string of pearls. The spirit and intent of the program should be retained throughout. There should be neither too little or too much, neither needless loops nor useless variables, neither lack of structure nor overwhelming rigidity." - The Tao of Programming, 4.1 - Geoffrey James

Re: How can I delete an element in a foreach cycle?
by shmem (Chancellor) on Feb 27, 2010 at 10:55 UTC

    TIMTOWTDI. You could remember the indices and splice the goners out, highest first:

    my @goners; for (0 .. $#ARGV) { unshift @goners, $_ if $ARGV[$_] eq 'hello'; } splice @ARGV, $_, 1 for @goners;

    update: $#ARGV instead of @ARGV-1 as per dsheroh's comment below.

      Any particular reason for @ARGV-1 instead of $#ARGV?

        Other than TIMTOWTDi, no ;-) But $#ARGV is better, since it needn't be calculated. - corrected, thanks.

      Or just go backwards...

      for my $i ( reverse 0 .. $#ARGV ) { splice( @ARGV, $i, 1 ) if $ARGV[$i] eq 'hello'; }

      www.jasonkohles.com
      We're not surrounded, we're in a target-rich environment!
Re: How can I delete an element in a foreach cycle?
by repellent (Priest) on Mar 01, 2010 at 03:55 UTC
    my @array = qw(hi hello howdy); my @hellos = grep_and_remove { $_ eq 'hello' } @array; use Data::Dumper; print Dumper \@array; __END__ $VAR1 = [ 'hi', 'howdy' ];

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://825596]
Approved by kennethk
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others about the Monastery: (5)
As of 2024-04-18 13:53 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found