Beefy Boxes and Bandwidth Generously Provided by pair Networks
Just another Perl shrine
 
PerlMonks  

Finding list elements in other lists.

by Anonymous Monk
on Oct 17, 2006 at 06:08 UTC ( [id://578677]=perlquestion: print w/replies, xml ) Need Help??

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

I have an array that will be tested. In the example code below, @true should come out as 'true' and @false should come out as 'false';

A tested array will test true if elements in its list exist it both the @p_types and @c_types arrays.

If the tested array only contains elements from one of the lists and not the other, then it's false.

I'be been trying to do this elgantly for several hours with grep but can't quite get it. I'm just having brain block, I'm even embarassed to post this under my name. How do I get this code to work properly and look elegant?

True Code
#!/usr/bin/perl -w use strict; my @p_types = qw( pam php kph ); my @c_types = qw( cam c1 c2 ); my @true = qw( pam c2 ); # Gets true my @false = qw( cam c2 ); # Gets false my @p_type = grep $_, @p_types foreach @true; my @c_type = grep $_, @c_types foreach @true; print "Array consists of elements from both arrays\n" if ( @p_type && +@c_type );

False Code
#!/usr/bin/perl -w use strict; my @p_types = qw( pam php kph ); my @c_types = qw( cam c1 c2 ); my @true = qw( pam c2 ); # Gets true my @false = qw( cam c2 ); # Gets false my @p_type = grep $_, @p_types foreach @false; my @c_type = grep $_, @c_types foreach @false; print "Array consists of elements from both arrays\n" if ( @p_type && +@c_type );

Replies are listed 'Best First'.
Re: Finding list elements in other lists.
by davido (Cardinal) on Oct 17, 2006 at 06:25 UTC

    The following post presents three possible solutions, basically listed in the order in which they came to my mind. The second is probably my favorite, but they each are worth looking at.

    Here's a little snippet that uses hashes for quick lookups within @p_types and @c_types. It uses these lookup tables to test the elements within @true and @false to see if they meet the criteria.

    use strict; use warnings; my( @p_types ) = qw/ pam php kph /; my( @c_types ) = qw/ cam c1 c2 /; my @true = qw/ pam c2 /; my @false = qw/ cam c2 /; my %p_hash; @p_hash{@p_types} = (); my %c_hash; @c_hash{@c_types} = (); print "\@true is ", ( qualify( \@true, \%p_hash, \%c_hash ) ) ? "True.\n" : "False.\n"; print "\@false is ", ( qualify( \@false, \%p_hash, \%c_hash) ) ? "True.\n" : "False.\n"; sub qualify { my( $test_aref, $p_href, $c_href ) = @_; if( grep{ exists $p_href->{$_} } @{$test_aref} and grep{ exists $c_href->{$_} } @{$test_aref} ) { return 1; } else { return 0; } }

    I think this is what you're after. It would be neater to create an object where the constructor builds the two test hashes, and qualify() becomes an object method taking the target array as an argument. That would help to encapsulate and generalize the hashes, while still preserving them to be tested against various target arrays. I'll post an example in a bit.

    Update:
    And here's an example of an object oriented technique that generalizes the set hashes and encapsulates the test sets for repeated testing as long as their object stays in scope.

    package All_In; use strict; use warnings; sub new { my $class = shift; my( $p_types, $c_types ) = @_; my( %p_hash, %c_hash ); @p_hash{ @{$p_types} } = (); @c_hash{ @{$c_types} } = (); my $self = {}; $self->{p_hash} = \%p_hash; $self->{c_hash} = \%c_hash; bless $self, $class; } sub qualify { my( $self, @target ) = @_; if( grep{ exists $self->{p_hash}{$_} } @target and grep{ exists $self->{c_hash}{$_} } @target ){ return 1; } else { return 0; } } 1; package main; use strict; use warnings; my $sets = All_In->new( [ qw/ pam php kph / ], [ qw/ cam c1 c2 / ] ); my @true = qw/ pam c2 /; my @false = qw/ cam c2 /; print "\@true is ", $sets->qualify( @true ) ? "True.\n" : "False.\n"; print "\@false is ", $sets->qualify( @false ) ? "True.\n" : "False.\n";

    Update 2:
    And how could I pass on an opportunity to tinker with Quantum::Superpositions? The following solution is certanly much easier to follow, but bound to be the least efficient from a runtime standpoint. But its simplicity is very enticing:

    use strict; use warnings; use Quantum::Superpositions; my @p_types = qw/ pam php kph /; my @c_types = qw/ cam c1 c2 /; my @true = qw/ pam c2 /; my @false = qw/ cam c2 /; print "\@true is ", qualify( \@true, \@p_types, \@c_types ) ? "True.\n" : "False.\n"; print "\@false is ", qualify( \@false, \@p_types, \@c_types ) ? "True.\n" : "False.\n"; sub qualify { my( $target, $p_types, $c_types ) = @_; my $quantum_target = any( @{$target} ); return 1 if ( $quantum_target eq any( @{$p_types} ) and $quantum_target eq any( @{$c_types} ) ); return 0; }

    Dave

      I like your package solution, as well as the arguments for it. This package can easily be extended to work for any number of arrays, not only two. The calling code stays the same.
      package All_In; use strict; use warnings; sub new { my $class = shift; my $self = []; for my $aref (@_) { my %h; @h{ @$aref } = (); push @$self, \%h; } bless $self, $class; } sub qualify { my $self = shift; for my $href (@$self) { return 0 if ! grep { exists $href->{$_} } @_; } return 1; } 1;

      -- Hofmator

      Code written by Hofmator and posted on PerlMonks is public domain. It is provided as is with no warranties, express or implied, of any kind. Posted code may not have been tested. Use of posted code is at your own risk.

Re: Finding list elements in other lists.
by roman (Monk) on Oct 17, 2006 at 08:23 UTC
    use strict; my @p_types = qw( pam php kph ); my @c_types = qw( cam c1 c2 ); my @true = qw(pam c2); #@true = qw(cam c2); #@true = q(pam php); # intermediate hash my %true = map { ( $_ => 1 ) } @true; if ( grep( $true{$_}, @p_types ) && grep( $true{$_}, @c_types ) ) { print "Opt A: Array consists of elements from both arrays\n"; } # "pure" grep if (( grep { my $type = $_; grep( $_ eq $type, @true ) } @p_types ) && (grep { my $type = $_; grep( $_ eq $type, @true ) } @c_types ) ) { print "Opt B: Array consists of elements from both arrays\n"; }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://578677]
Approved by prasadbabu
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others pondering the Monastery: (4)
As of 2024-04-26 00:01 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found