http://qs321.pair.com?node_id=208423

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

Here's what I want to accomplish:

my @arr = qw(aa bb cc); my $var = "aa"; yoink(@arr, $var); print "@arr\n"; # now, @arr is qw(bb cc);

The yoink() function would remove from the array any elements that match the $var variable.

Is there a function to accomplish this already? I'm about to write "yoink()", but before I do, I thought I'd check with the resident geniuses around here.

Thanks as always.

Replies are listed 'Best First'.
Re: how to rm an element from an array?
by I0 (Priest) on Oct 28, 2002 at 01:25 UTC
    @arr=grep $_ ne $var, @arr;
Re: how to rm an element from an array?
by graff (Chancellor) on Oct 28, 2002 at 01:41 UTC
    I'd suggest a different call sequence for "yoink()":
    yoink( \@arr, $var ); # pass an array ref and a scalar
    The sub definition could go something like this, if you don't mind making a copy of the array in order to do the work:
    sub yoink { my ($arr_ref, $kill_pattern) = @_; my @out_arr; for (@$arr_ref) { push @out_arr, $_ unless (/^$kill_pattern$/) } @$arr_ref = @out_arr; }
    The "yoink" could also use the "splice" function (see perldoc -f splice) to edit the array in place; I suspect the "optimal" choice would depend on array size and number of elements being removed -- with big arrays, doing just a few edits will go best with splice, but doing lots of edits might go quicker using selective copying to a separate array. (For small arrays, there won't be a noticeable difference.)

    update: IO's solution is better than mine, of course. (I just forgot that I should've known better...)

      Why not a combination of the two solutions, something like:

      sub yoink { my ($array_ref, @kill_patterns) = @_; foreach my $kp (@kill_patterns) { @{$array_ref} = grep $_ ne $kp, @{$array_ref}; } }
      That way, you could call it with one or more kill patterns, such as &yoink(\@arr, $kp), or &yoink(\@arr, $kp1, $kp2, $kp3), or so forth... (At least, I think it would work....)
        If you always want to modify the original array, you could use function prototypes (let the scolding begin!) and use yoink like a builtin. That way, you wouldn't have to keep passing \@arr as a ref like that. However, if you want to keep the original array intact and let yoink return a separate copy with the proper yoinkage, then this is certainly not at all what you want.
        sub yoink(\@$) { my ($array_ref, $remove) = @_; @$array_ref = grep { $_ ne $remove } @$array_ref; } my @arr = qw/un deux trois quatre cinq six sept huit/; # no passing of \@arr -- oh, so pretty! yoink @arr, 'deux'; print "@arr\n"; __END__ un trois quatre cinq six sept huit

        blokhead

Re: how to rm an element from an array?
by Revelation (Deacon) on Oct 28, 2002 at 03:30 UTC
    I don't do this personally, but the delete function can be used, instead of grep, if you want to preserve the array indices:
    perldoc -f delete
    Given an expression that specifies a hash element, array element, hash slice, or array slice, deletes the specified element(s) from the hash or array. In the case of an array, if the array elements happen to be at the end, the size of the array will shrink to the highest element that tests true for exists() (or 0 if no such element exists).
    for (0..$#arr) {delete($arr[$_]) if $arr[$_] eq $var;};
    I'd use grep, otherwise (IO has provided an example of how to use grep).

    Gyan Kapur
    gyan.kapur@rhhllp.com
Re: how to rm an element from an array?
by jdporter (Paladin) on Oct 28, 2002 at 17:53 UTC
    Unless you have bigger plans for that yoink(), I'd scrap it altogether and just use straight, natural, intrinsic perl.

    Start with IO's solution. Then you can extend the pattern-matching part as needed. And avoid the obfuscation of intent caused by using yoink().