Beefy Boxes and Bandwidth Generously Provided by pair Networks
No such thing as a small change

Yet More fun With Array Indices

by Elgon (Curate)
on Sep 14, 2002 at 16:31 UTC ( #197886=perlquestion: print w/replies, xml ) Need Help??
Elgon has asked for the wisdom of the Perl Monks concerning the following question:

Dear Monks,

on a similar point to Limbic~Region's (sounds like a Culture shipname to me) regarding array indices, I have a question on this one: Let's say that I have an array from which I wish to remove a record (in the sense of splice - like a DNA base deletion.) I am using the following code:

for ($index = 0; $index <= (scalar(@array) - 1); ++$index) { if ($array[$index] == $whatever) { splice ("array, $index, 1); } }

Or it could be done this way, I suppose...

$index = 0; foreach $thing(@array) { if ($thing == $whatever) { splice (@array, $index, 1); } ++$index; }

Personally I find the for loop somewhat messy and would prefer a tidy foreach instead, however the second way seems equally crufty. Is there simply a far more elegant way of doing this of which I am unaware? Limbic~Region's hypothetical function does do what I want but I was wondering if there was some *magic variable* used by Perl as an iterator in foreach loops somewhat like that found in the each function for use with hashes.

Of the solutions so far, I prefer the first but am very willing to listen to other ideas.


"Rule #17 of Travel: Never try and score dope off Hassidic Jews while under the impression that they are Rastafarians."
       - Pete McCarthy, McCarthy's Bar

Replies are listed 'Best First'.
Re: Yet More fun With Array Indices
by japhy (Canon) on Sep 14, 2002 at 16:48 UTC
    But you don't need to do this -- you can just use grep() and be done with it: @ok = grep !bad(), @orig;.

    Also, it's considered dangerous to resize an array while looping over it (Perl-style), and difficult to resive an array while looping over it (index-style).

    my $i = 0; while ($i < @array) { if (REMOVE) { splice @array, $i, 1 } else { ++$i } }
    See? Only increment the index if the element's not being removed. This is why grep() is king. grep() is the True Way.

    Jeff[japhy]Pinyan: Perl, regex, and perl hacker, who'd like a job (NYC-area)
    s++=END;++y(;-P)}y js++=;shajsj<++y(p-q)}?print:??;

Re: Yet More fun With Array Indices
by blokhead (Monsignor) on Sep 14, 2002 at 16:51 UTC
    If you're just removing items that match/don't match a pattern, grep is the easiest choice:
    my @array = qw/1 2 3 4 5 6 7 8 9 10 11 12/; my $pattern = qr/2/; @array = grep { ! /$pattern/ } @array; # @array now contains qw/1 3 4 5 6 7 8 9 10 11/


      Bullseye - I don't understand why everyone else got stuck on the for loop. Some minor points though: if you're using patterns for matches, you should consider using anchors: @array = grep { ! /^$pattern$/ } @array;
      In this case it's better to just use a comparison: @array = grep $_ ne $whatever, @array;

      Makeshifts last the longest.

      Since Elgon's original code appeared to be doing numeric tests, it's worth pointing out that grep will work with numeric operations as well as string matches. For instance:
      @a=(1,2,3,4,5); @b=grep {int($_/2) == 1} @a; print join $/,@b,"";
      prints out the values 2 and 3.

      Once again my faith in Perlmonks is vindicated - Deliciously lazy, redolent with hubris and positively teeming with impatience. Cracking solution.

      Thanks, Elgon.

      "Rule #17 of Travel: Never try and score dope off Hassidic Jews while under the impression that they are Rastafarians."
             - Pete McCarthy, McCarthy's Bar

Re: Yet More fun With Array Indices
by elusion (Curate) on Sep 14, 2002 at 16:47 UTC
    Something like this perhaps?
    foreach my $index (0..$#array) { if ($array[$index] == $whatever) { splice @array, $index, 1; } }
    Or, if you use $_ and a for loop:
    for (0..$#array) { if ($array[$_] == $whatever) { splice @array, $_, 1; } }
    One last choice to build: using an if statement modifier
    for (0..$#array) { splice @array, $_, 1 if $array[$_] == $whatever; }
    Except all of these are really flawed, because it skips elements. Removing an element makes all the future indices one shorter than they really are. An array of (1, 2, 1, 1, 2) will be changed to (2, 1, 2) if $whatever equals 1, because it skips around. To fix this, iterate backwards through the array, like so:
    for (reverse 0..$#array) { splice @array, $_, 1 if $array[$_] == $whatever; }

    elusion :

Log In?

What's my password?
Create A New User
Node Status?
node history
Node Type: perlquestion [id://197886]
Approved by Zaxo
[Corion]: Discipulus: As is great tradition, just build another level on it ;)
[Discipulus]: yes, bikers included. ;=)

How do I use this? | Other CB clients
Other Users?
Others taking refuge in the Monastery: (5)
As of 2017-02-28 10:42 GMT
Find Nodes?
    Voting Booth?
    Before electricity was invented, what was the Electric Eel called?

    Results (399 votes). Check out past polls.