peppiv has asked for the wisdom of the Perl Monks concerning the following question:
Happy Holidays!
Just seeing who else spent all their time off during the year and are stuck working the holidays. For those of you left, let's see if we can give this descent young fella a hand.
My program fills an array with a text file that looks like this:
1001,choochoo
1002,candycane
1003,sockpuppet
etc.
The actual amount of elements in this array may vary, so I can't say $array[2] to be sure of getting a sockpuppet. I need to search the array. Fortunately, I know that the data I'm looking for begins with 1006. What I'd like to do is put the value I'm looking for into a string for further use. (info that needs to be sent back through a URL - junk like that).
So, exposing my weak knowledge of grep and search, I tried the following:
my $answer= grep /^1006,/, @array;
my $thing = substr($answer, 6,6);
I know, I know. Pathetic ain't it?
Please help!
peppiv
Re: Getting element of array with a match
by broquaint (Abbot) on Dec 23, 2002 at 14:49 UTC
|
my %hash = map { chomp; split /,/ } <DATA>;
print $hash{1006},$/;
__DATA__
1001,choochoo
1002,candycane
1003,sockpuppet
1004,choochoo
1005,candycane
1006,sockpuppet6
1007,foo
1008,bar
Or am I missing the point?
HTH
_________ broquaint | [reply] [d/l] |
Re: Getting element of array with a match
by tachyon (Chancellor) on Dec 23, 2002 at 14:42 UTC
|
While grep is handy it it not efficient as you always search the whole array even if you find a match in the first element. Assuming your '100X' are unique product ids this is probably the most efficient way (although by no means the only way) to do it (the last means we stop searching at the first match. If we find a match string will be defined. If there is not match it will be undef. If you have comma separated data split will work fine. Substring works on the assumption of fixed width records so if someone enters say '1010  ' you would end up getting ' ,string' back which is not what you want. The correct syntax to get all the stuff after 5 chars (4 digits and the comma) is $string = substr $line, 5; Your grep is broken because when you say $scalar = grep... you get the number of matches not the actual array elements(s). If you call grep in array context @ary = grep... then you get an array of all the elements that matched - if there is only one it will be $ary[0]. Many functions in perl exhibit this schizophrenic behaviour - it is called scalar/array context.
my @ary = <DATA>;
my $find_id = 1006;
my ($code, $string );
for my $line( @ary ) {
next unless $line =~ m/^$find_id/;
chomp $line;
( $code, $string ) = split ',', $line;
last;
}
if ( $string ) {
print "Found '$string'\n";
}
else {
print "No match!\n";
}
__DATA__
1001,choochoo
1002,candycane
1003,sockpuppet
1004,choochoo
1005,candycane
1006,sockpuppet6
1007,foo
1008,bar
cheers
tachyon
s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print
Fixed errant [] chars dvergin 2002-12-23
| [reply] [d/l] [select] |
|
my $id = 1006;
my $answer;
for (@data) {
last if ($answer) = /^\Q$id\E\s*,(.*)/o;
}
— Arien
Update: Added /o and allowed for optional whitespace between id and comma per tachyon's worries below. | [reply] [d/l] [select] |
|
Sure, that's the sort of way I would usually code it myself but you then have to (potoentially) explain how that single line works. There is also an implicit requirement in your code that the comma imediately follows the id as well as having no leading whitespace. All probably true but... You should have either used qr// or /o in your code BTW ;-)
For those that are wondering the \Q...\E is the regex version of quotemeta which ensures that interpolated strings do not blow up your regex when they contain metachars. It is always sound practice to use \$Q$string\E in regexes.
cheers
tachyon
s&&rsenoyhcatreve&&&s&n.+t&"$'$`$\"$\&"&ee&&y&srve&&d&&print
| [reply] |
|
|
Re: Getting element of array with a match
by dreadpiratepeter (Priest) on Dec 23, 2002 at 14:51 UTC
|
Actually a simple loop might be cleaner and clearer in this instance:
my $thing;
foreach (@array) {
if (/^1006,(.*)/) {
$thing=$1;
last;
}
}
or even:
my $thing;
foreach (@array) {
my ($num,$value) = split /,/;
if ($num == 1006) {
$thing=$value;
last;
}
}
BTW, your code above won't work because grep returns the number of matches in scalar context.
-pete
"Worry is like a rocking chair. It gives you something to do, but it doesn't get you anywhere." | [reply] [d/l] [select] |
Re: Getting element of array with a match
by robartes (Priest) on Dec 23, 2002 at 14:53 UTC
|
Close, but grep returns a list, not a scalar. Forcing the return value in a scalar, as you do, will get you the number of elements in it, or in your case the number of elements starting with 1006. Also, you might look into split to get to the second field in the element:
print (split /,/,$_)[1] for grep /^1006/, @array; #UNTESTED
As a better solution for this problem, consider using a hash instead of an array to store your data (that is, if the first field in your data is unique). Hashes are built for fast text searches:
use strict;
my %hash=qw(1001 choochoo 1002 candycane 1003 sockpuppet);
print $hash{'1003'};
__END__
sockpuppet
CU Robartes- | [reply] [d/l] [select] |
|
A variation on the theme of grep
my @ar = map { chomp; $_ } <DATA>;
print +(split /,/,
$ar[ grep { /^(\d+)/ and $1 < 1006 } @ar ])[1], $/;
__DATA__
1001,choochoo
1002,candycane
1003,sockpuppet
1004,choochoo
1005,candycane
1006,sockpuppet6
1007,foo
1008,bar
This of course blindly assumes that you're data is a linear, contiguous set of numbers.
HTH
_________ broquaint | [reply] [d/l] |
|
Thank you everyone for the help and education. I got what I needed and everything works perfectly!
peppiv
| [reply] |
|
|