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

Dear Masters,
Given an AoA and a array reference, I want to check if the array ref is a memeber of the AoA.
How come my code below doesn't do the job correctly?

And is there any efficient way to do this?
In particular I need to check against AoA of size around 2000 entries.
use strict; use Data::Dumper; use List::Compare; my @AoA = ( [ 'beethoven', 'berlioz', 'strauss' ], [ 'wagner', 'mozart', 'brahms' ], [ 'sibelius', 'elgar', 'dvorak' ] ); # Should return 1 (True) my $test = [ 'wagner', 'mozart', 'brahms' ]; # Should return 0 (False) my $test2 = [ 'faure', 'debussy', 'stravinksy' ]; print check_if_inst_occur_already(\@AoA,$test),"\n"; print check_if_inst_occur_already(\@AoA,$test2),"\n"; sub check_if_inst_occur_already { my ( $store, $inst_set ) = @_; INST_ALREADY: foreach my $ins_already ( @{$store} ) { my $lc = List::Compare->new( $ins_already, $inst_set ); my $eq = $lc->is_LeqvlntR; print "EQ : $eq\n"; if ( $eq == 0 ) { return 0; } else { return 1; last INST_ALREADY; } } }


---
neversaint and everlastingly indebted.......

Replies are listed 'Best First'.
Re: Checking if an Array is a Member of an AoA
by jhourcle (Prior) on Dec 23, 2005 at 13:47 UTC

    I'm not familiar with list compare, so it's possible there's something wrong with the syntax you're using, but the item that I noticed is that you're returning 0 within the loop.

    Normally, you'd want to use logic like:

    sub test_match_subroutine { my ( $item, $possible_matches ) = @_; foreach my $test_case ( @$possible_matches ) { return 1 if is_a_match($item, $test_case); } return 0; }

    Note -- we don't fail until all cases fail ... not when the first item fails.

Re: Checking if an Array is a Member of an AoA
by holli (Abbot) on Dec 23, 2005 at 14:33 UTC
    I don't know the exact specifications of your problem, but if I hear "lookup" my perl sense cries "HASH!!!". So in your case I would probably do
    • calculate MD5 checksum of the arrayref you want to look up
    • check if that checksum is present in a hash
    • if so, discard the array
    • if not, add checksum to hash and add arrayref to array


    holli, /regexed monk/
      calculate MD5 checksum of the arrayref you want to look up
      Hi Holli,
      Can you give example of how to do that? Is there any specific module that does this?

      ---
      neversaint and everlastingly indebted.......

        I'm not holli's alterego, but I believe holli would point you to use Digest;, which can calculate (among others) MD5 signatures.

        swampyankee is correct. Here is some code that shows the principle:
        use strict; use warnings; use Data::Dumper; use Digest::MD5 qw(md5); my %lookup; my @AoA = ( ["a", "b", "c"], ["d", "e", "f"], ["a", "b", "c"], ["g", "h", "i"], ["a", "b", "c"], ["g", "h", "i"], ); #Build a lookup hash from AoA, values of the hash will be #an arrayref to the positions where a given array was found my $index = 0; for ( @AoA ) { my $k = md5(Dumper($_)); push @{$lookup{$k}}, $index++; } $index = 0; #test data my @test = ( ["a", "b", "c"], ["d", "e", "f"], ["g", "h", "i"], ["x", "y", "z"], ); #loop tests for ( @test ) { print "Test ", $index++, ": ", join (", ", @{ref_exists($_)}), "\n +"; } #returns the positions or undef when the arrayref is not in AoA sub ref_exists { return $lookup{md5(Dumper(shift))} || []; }
        Note that, because I use Data::Dumper for creating the digest, the technique should work for deep structures also, not just for plain arrays of strings.


        holli, /regexed monk/
Re: Checking if an Array is a Member of an AoA
by educated_foo (Vicar) on Dec 23, 2005 at 14:35 UTC
    I usually find it easier to compare lists using hashes, like so:
    sub different { my ($h1, $h2) = @_; return 1 unless keys %$h1 == keys %$h2; for (keys %$h1) { return 1 unless exists $h2->{$_}; } return 0; } sub member { my ($a, $aoa) = @_; my %as; undef @as{@$a}; for my $x (@$aoa) { my %bs; undef @bs{@$x}; return 1 unless different \%as, \%bs; } return 0; }
    Depending on what else you're doing with the AoA, you might want to use an array of hashes instead.
Re: Checking if an Array is a Member of an AoA
by myuji (Acolyte) on Dec 23, 2005 at 15:11 UTC
    One answer to Check if an Array is a Member of an AoA is :
    sub check_if_inst_occur_already { my ( $store, $inst_set ) = @_; foreach my $ins_already ( @{$store} ) { return 1 if is_equivalent($ins_already, $inst_set); } return 0; } sub is_equivalent{ my ($aa, $bb) = @_; return 0 unless @$aa == @$bb; for( 0 .. @$aa-1){ return 0 unless $aa->[$_] eq $bb->[$_]; } return 1; }
Re: Checking if an Array is a Member of an AoA
by calin (Deacon) on Dec 23, 2005 at 18:14 UTC

    By "member array ref" of an AoA you can understand two things:

    1. The "member array ref" points to the same memory object as the element in the AoA points to.
    2. The "member array ref" points to a different memory object which has an identical "printable representation", or other criterion of comparison you may see fit.

    From your post, I assume you want (2). Here's an implementation of both (1) an (2) together with a test. Be aware that my implementation of (2) is simple - it relies on stringification by joining with $;, which does not work if the arrays in the AoA are themselves deep data structures and may give incorrect results if you use the value of $; in your subarray values.

    #!/usr/bin/perl use strict; use warnings; sub member_as_object { my $aref = shift; my $AoAref = shift; return grep {$aref eq $_} @{$AoAref}; } sub stringify { return join $;, @{(shift)}; } sub member_as_stringified_value { my $astring = stringify(shift); my $AoAref = shift; return grep {$astring eq stringify($_)} @{$AoAref}; } # building a test case below my @AoA = ( [ 'beethoven', 'berlioz', 'strauss' ], [ 'wagner', 'mozart', 'brahms' ], [ 'sibelius', 'elgar', 'dvorak' ] ); my %arrays = ( member_obj => $AoA[1], member_copy => [ @{$AoA[1]} ], non_member => [ qw/foo bar baz/ ] ); my %compare_subs = ( member_as_object => \&member_as_object, member_as_stringified_value => \&member_as_stringified_value ); for my $csub (sort keys %compare_subs) { for my $array (sort keys %arrays) { my $result = $compare_subs{$csub}->($arrays{$array}, \@AoA) ? 'true' : 'false +'; print "$csub($array) = $result\n"; } }