I need to walk over an array, and conditionally delete elements, in Perl. I know about slice
, but am not sure how to use it in a foreach
context.
In Ruby, there is reject!
:
foo = [2, 3, 6, 7]
foo.reject! { |x| x > 3 }
p foo # outputs "[2, 3]"
Is there a Perl equivalent?
@foo = (2, 3, 6, 7);
@foo = grep { $_ <= 3 } @foo;
print @foo; # 23
As the other answers suggest, grep
is typically all you need.
However, it is possible with perl prototypes to code a function that, like ruby's Array#reject!
:
Usage is:
@foo = (2, 3, 6, 7); # Void context - modify @foo in place
reject { $_ > 3 } @foo; # @foo is now (2, 3)
@foo = (2, 3, 6, 7); # Scalar context - modify @foo in place
$n = reject { $_ > 3 } @foo; # @foo is now (2, 3), $n is length of modified @foo
@foo = (2, 3, 6, 7); # Array context - return a modified copy of @foo
@cpy = reject { $_ > 3 } @foo; # @cpy is (2, 3), @foo is (2, 3, 6, 7)
Implementation:
sub reject(&\@) {
my ($block, $ary) = @_;
# Return a copy in an array context
return grep {! $block->() } @$ary if wantarray;
# Otherwise modify in place. Similar to, but not
# quite, how rb_ary_reject_bang() does it.
my $i = 0;
for (@$ary) {
next if $block->();
($ary->[$i], $_) = ($_, $ary->[$i]); # swap idiom to avoid copying
$i++; # possibly huge scalar
}
$#$ary = $i - 1; # Shorten the input array
# We differ from Array#reject! in that we return the number of
# elements in the modified array, rather than an undef value if
# no rejections were made
return scalar(@$ary) if defined(wantarray);
}
vinko@parrot:/home/vinko# more reject.pl
my @numbers = (2, 3, 6 ,7);
print join(",", grep { $_ <= 3 } @numbers)."\n";
vinko@parrot:/home/vinko# perl reject.pl
2,3
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With