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

I have the following set of aarays:
@lane01_1= (200900..202543); @lane01_2= (202544..204187); @lane01_3= (204188..205831); @lane01_4= (205832..207475); @lane01_5= (207476..210119); @lane01_6= (210120..211763); @lane01_7= (211764..213407); @lane01_8= (213408..215051);
Now I have a log file which is parsed for a certain number which I store in a variable $vector. Now if $vector matches any of the elements in @lane01_1 I need to print "1", If it matches the elements in @lane01_2, I need to print "2" and so on and so forth. Whats the best way to do this? Should I push these arrays in to a hash (and whats the easiest way to do that?) or can I do it without using a hash? Thanks!!

Replies are listed 'Best First'.
Re: match with elements in array
by kennethk (Abbot) on May 18, 2010 at 22:15 UTC
    Can we assume that your lists will always be contiguous lists of numbers? If so, you can skip the construction itself and just test limits:

    #!/usr/bin/perl use strict; use warnings; my @line01 = ([200900, 202543], [202544, 204187], [204188, 205831], [205832, 207475], [207476, 210119], [210120, 211763], [211764, 213407], [213408, 215051], ); my $vector = 206000; for my $i (0 .. $#line01) { if ($vector >= $line01[$i][0] and $vector <= $line01[$i][-1]) { print $i+1 . "\n"; } }

    Note that I have swapped from a series of arrays into an array of arrays (see perllol and/or perlreftut) so the testing code does not need to be repeated 8 times.

    If the constraints are different than what I've assumed, you can do this a number of ways, like constructing a hash or regular expressions, which will be of varying appropriateness depending on the actual case.

      Worked perfectly :) AWESOME! Thanks!
Re: match with elements in array
by Marshall (Canon) on May 18, 2010 at 22:49 UTC
    I like kennethk's idea. But since you asked about a hash way, I'll show a way...see below. This of course is a memory hog! But 14,000 hash keys wouldn't necessarily be considered "huge". The code to build the hash look up table is simple and then you would just query it for values of $vector. How efficient this thing has to be is of course a matter of your application. Something like this might be more appropriate if the "lane buckets" weren't contiguous or if there were multiple ranges associated with each lane.
    #!/usr/bin/perl -w use strict; use Data::Dumper; my @lanes = ( [(200900..202543)], [(202544..204187)], [(204188..205831)], [(205832..207475)], [(207476..210119)], [(210120..211763)], [(211764..213407)], [(213408..215051)], ); my %hash; my $lane =1; foreach my $aref (@lanes) { @hash{@$aref} = ($lane) x @$aref; $lane++; } my $vector = 210121; print $hash{$vector}; #prints 6
Re: match with elements in array
by choroba (Cardinal) on May 18, 2010 at 22:28 UTC
    Are you sure your intervals are correct? The difference between endpoints is 1643 except in group 5, where it is 2643.
Re: match with elements in array
by johngg (Canon) on May 19, 2010 at 12:59 UTC

    You could do this using compiled regular expressions for each range as keys to a lookup hash.

    use strict; use warnings; use 5.010; my %lanes = ( 1 => [ 200900 .. 202543 ], 2 => [ 202544 .. 204187 ], 3 => [ 204188 .. 205831 ], 4 => [ 205832 .. 207475 ], 5 => [ 207476 .. 210119 ], 6 => [ 210120 .. 211763 ], 7 => [ 211764 .. 213407 ], 8 => [ 213408 .. 215051 ], ); my %lookup = map { local $" = q{|}; ( qr{^(?:@{ $lanes{ $_ } })$}, $_ ) } keys %lanes; my @toTest = ( 175924, 203786, 208293 ); OUTER: foreach my $test ( @toTest ) { INNER: foreach my $rxCheck ( keys %lookup ) { if ( $test =~ $rxCheck ) { say qq{$test -- $lookup{ $rxCheck }}; next OUTER; } } say qq{$test -- No match!}; }

    The output.

    175924 -- No match! 203786 -- 2 208293 -- 5

    I hope this is of interest.

    Cheers,

    JohnGG

Re: match with elements in array
by dineed (Scribe) on May 19, 2010 at 05:55 UTC

    Here is another version using the arrays as listed in OP and a reference that loops through each array. You could add logic to break out of the loops once the value is found.

    Updated again... left out $found declaration and initialization (fat fingers).

    #!c:\perl\bin\perl.exe use strict; use warnings; my @lane01_1 = (200900..202543); my @lane01_2 = (202544..204187); my @lane01_3 = (204188..205831); my @lane01_4 = (205832..207475); my @lane01_5 = (207476..210119); my @lane01_6 = (210120..211763); my @lane01_7 = (211764..213407); my @lane01_8 = (213408..215051); my $arrayref; my $buildvar; my $vector = 209456; my $found = 0; while(! $found) { $buildvar = "\\\@" . "lane01_" . $i; $arrayref = eval($buildvar); if(! defined @$arrayref) { print("vector: $vector not found\n"); last; } for(my $j = 0; $j < (scalar(@$arrayref)); $j++) { if($$arrayref[$j] == $vector) { print("lane # is $i\n"); $found = 1; } } $i++; } OUTPUT: lane # is 5

    Update #1: Removed some values from testing

    Update #2: Included output.

    Update #3: Couldn't take it - replaced outer for loop with a while loop and a bool. This also required a test to make sure @$arrayref references a valid array. If it's not, then there is no match for $vector. Original for loop included below.

    for (my $i = 1; $i <= 8; $i++) { $buildvar = "\\\@" . "lane01_" . $i; $arrayref = eval($buildvar); for(my $j = 0; $j < (scalar(@$arrayref)); $j++) { if($$arrayref[$j] == $vector) { print("lane # is $i\n"); } } }