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

I am modifying an already existing program which has two arrays which are functionally connected.

Example:

element @a @b 0 1 aa 1 2 aa 2 1 ab 3 1 ac 4 2 ac 5 2 ad 6 1 ac 7 2 ac
Given a paired input ($a,$b), I need to test if the paired elements exist anywhere in the arrays...
i.e. $a.$b=$a[$i].$b[$i]
where $i is the element number of the successful pair.
Ex: $a=1 and $b=ab would test true, but $a=1 and $b=ad would test false.

Obviously I could create a more intelligent system, but I wish to keep the code changes to a minimum, and the paired arrays are used throughout the entire code. That said, I want to keep them as is.

This is the current code

$found = 0; foreach $j (1..@a-1) { if ($a eq $a[$j] && $b eq $b[$j]) { $found = 1; last; } } unless ($found) {do something}
This works, but I find it kinda clunky. I've thought about 'greps' and 'maps' but I can't seem to come up with anything that is 'less' clunky.

Any ideas?

Sandy

UPDATE: fixed typo in code snippet

Replies are listed 'Best First'.
Re: Finding a match between two paired arrays
by broquaint (Abbot) on Jan 08, 2004 at 15:40 UTC
    Seems like a job for grep e.g
    if( grep { $a == $a[$_] && $b eq $b[$_] } 0 .. $#a ) { # do stuff here }
    See. the grep docs for more info.

    Update: and here's the cleaner approach using List::Util's first (as kindly pointed out by Zed_Lopez below)

    if( first { $a == $a[$_] && $b eq $b[$_] } 0 .. $#a ) { # do stuff here }
    HTH

    _________
    broquaint

      List::Util's first function will work, too, with the advantage of exiting as soon as something's found, saving the computation of searching all of both lists every time.

        As does the OP's already existant solution :)

Re: Finding a match between two paired arrays
by duff (Parson) on Jan 08, 2004 at 15:45 UTC

    The only thing "clunky" about it IMHO is the synthetic variable $found and even that's not so bad. You could the code it in a sub to isolate it a bit:

    # note, assumes we're in a scope that knows about @a and @b sub check_a_b { my ($a,$b) = @_; for (0..$#a) { return 1 if $a eq $a[$_] && $b eq $b[$_]; } return 0; }

    Or you could transmogrify the arrays to hashes but if you're updating the arrays throughout the code, you'll have to update the hashes too and that can be a bit of a pain.

Re: Finding a match between two paired arrays
by ysth (Canon) on Jan 08, 2004 at 17:10 UTC
    I would be awfully tempted just to use a hash, even if the arrays get updated such that the hash would need to be rebuilt each time:
    unless (exists { map { $a[$_].$b[$_] => $_ } 0..$#a }->{$a.$b}) { do_something(); }
    If the arrays stay the same, you only need to build the hash once:
    %ab = map { $a[$_].$b[$_] => $_ } 0..$#a; ... unless (exists $ab{$a.$b}) { do_something() }
      my @a = qw(1 11); my @b = qw(1a 2b); ($a,$b) = qw(11 a); print "Why am I printing?$/" unless (exists { map { $a[$_].$b[$_] => $ +_ } 0..$#a }->{$a.$b});

      Of course, if you can guarantee @a contains only numerals and @b contains no numerals, then your solution works as advertised. However, this solution does break down when either array can contain any character.

        Thanks for pointing that out. I was going to mention the limitation, but decided the OP's example was sufficiently exemplary.
Re: Finding a match between two paired arrays
by antirice (Priest) on Jan 08, 2004 at 16:30 UTC

    First, you skip index 0. Second, if anyone modifies $[, your @a-1 won't be the last element. Anyhow, let's take a crack at it:

    #!/usr/bin/perl -wl use strict; my @a = qw(1 2 1 1 2 2 1 2); my @b = qw(aa aa ab ac ac ad ac ad); print "first" if find_pair(1,"aa",\@a,\@b); print "not printed" if find_pair(2,"ax",\@a,\@b); print "not printed" if find_pair(4,"ax",\@a,\@b); print "second" if find_pair(2,"ac",\@a,\@b); print "third" if find_pair(2,"ad",\@a,\@b); sub find_pair { my ($first,$second,$array1,$array2) = @_; $first eq $array1->[$_] && $second eq $array2->[$_] && return 1 for +$[..$#$array1 } __END__ first second third

    antirice    
    The first rule of Perl club is - use Perl
    The
    ith rule of Perl club is - follow rule i - 1 for i > 1

Re: Finding a match between two paired arrays
by kesterkester (Hermit) on Jan 08, 2004 at 15:52 UTC
    Here's a small subroutine that'll test for a matched pair.

    FWIW, using $a and $b as variable names in non-example code can get confusing pretty fast, especially if you're writing any sort routines.

    use warnings; use strict; our @arr_1 = qw/ 1 1 2 1 1 2 2 1 2 /; our @arr_2 = qw/ ab aa aa ab ac ac ad ac ac /; my ( $input1, $input2 ) = ( 1, 'ab' ); my ( $input3, $input4 ) = ( 1, 'ad' ); print "test1: ", find_match ( $input1, $input2 ), "\n"; print "test2: ", find_match ( $input3, $input4 ), "\n"; #unless ( find_match ( search pair ) ) { do something } sub find_match { my ( $in1, $in2 ) = @_; return scalar grep { $in1 eq $arr_1[$_] && $in2 eq $arr_2[$_] } 0..scalar @a +rr_1 - 1; }
Re: Finding a match between two paired arrays
by Sandy (Curate) on Jan 08, 2004 at 20:15 UTC
    Thanks guys (gals?) you've been very helpful. Your answers also helped me figure out some other similar problems.