How can I select certain elements from a Perl array?
I want to search for all elements in an ar开发者_如何学Pythonray which have the same starting set of characters as an element in another array. To make it clear:
@array = ("1a","9","3c");
@temp =("1","2","3");
I want to print only 1a and 3c. When I try to use the following program it prints out all the elements in the array instead of the two I want:
foreach $word (@temp)
{
if( grep /^$word/ , @array)
{
print $_;
}
}
Any insights will be appreciated.
This answer will do what the OP wants as well as prevent any duplicates from printing on the screen through the use of a hash lookup.
#!/usr/bin/perl
use strict;
use warnings;
my @array = ("1a","9","3c","3c");
my @temp =("1","2","3");
my %dups_hash;
for my $w (@temp) {
my ($match) = grep /^$w/, @array;
# Need to check if $match is defined before doing the hash lookup.
# This suppresses error messages for uninitialized values; if defined($match) is
# false, we short circuit and continue in the loop.
if(defined($match) && !defined($dups_hash{$match})) {
print $match;
}
}
If you want to match the elements pairwise, you can do it this way:
for my $i (0..$#array) {
print $array[$i], "\n" if $array[$i] =~ /^$temp[$i]/
}
Otherwise you can use grep:
for my $i (0..$#array) {
print "$array[$i]\n" if grep /^$temp[$i]/, @array;
}
For this sort of problem, the trick is to not scan the array more than you have to. I think Knuth wrote an entire book about that. :) Often, we get stuck in these situations because we stick too closely to the thing we tried first.
You can construct a single regular expression from all of the patterns that you want to search then scan the array once:
use Regexp::Assemble;
my @array = qw( 1a 9 3c );
my @temp = qw( 1 2 3 );
my $ra = Regexp::Assemble->new;
$ra->add( @temp );
my $pattern = $ra->re;
print "pattern is [$pattern]\n";
print join ' ', grep /\A$pattern/ , @array;
This sort of thing works when you don't care which part of the pattern matches as long as it matches.
When I try to use the following program it prints out all the elements in the array instead of the two I want.
No it doesn't. As written, it prints nothing. With strict turned on, it prints "Global symbol "$temp" requires explicit package".
Fixing that obvious typo and turning on warnings, it prints "Use of uninitialized value $_ in print" twice.
Please don't waste our time, by showing us code that either doesn't compile doesn't do what you say. Don't retype code into this site - cut and paste the actual code that you're using.
The solution to your problem is going to be something like:
#!/usr/bin/perl
use strict;
use warnings;
my @array = ("1a","9","3c");
my @temp =("1","2","3");
foreach my $word (@temp) {
print grep /^$word/ , @array;
}
But there are probably more efficient ways of doing it.
map { print "$_\n" } grep { my $a = $_; grep {$a =~ /^$_/} @temp } @array
Basically, the outer grep
selects the elements for which 1 or more of the elements in @temp
matches the inner regex-- that is, it selects all elements which start with one (or more) of the elements in @temp
.
To avoid blank lines if grep return empty list :
#!/usr/bin/perl
use strict;
use warnings;
use 5.10.1;
my @array = qw(1a 9 3c 1g);
my @temp =(1, 2, 3);
foreach my $word(@temp) {
my @l = grep{/^$word/}@array;
say "@l" if @l;
}
Output :
1a 1g
3c
精彩评论