Beefy Boxes and Bandwidth Generously Provided by pair Networks
The stupid question is the question not asked
 
PerlMonks  

testing more than one item for equality

by jonnyfolk (Vicar)
on Oct 18, 2006 at 11:27 UTC ( #579032=perlquestion: print w/ replies, xml ) Need Help??
jonnyfolk has asked for the wisdom of the Perl Monks concerning the following question:

I'm trying to do the following:
if ( ($item1 or $item2) eq ($item3) ) { #do something; }
In fact I've had to workaround:
my $result = grep { $_ eq $item3 } ($item1, $item2); if ($result) { #do something }
Is there a more direct statement one can use?

Comment on testing more than one item for equality
Select or Download Code
Re: testing more than one item for equality
by davorg (Chancellor) on Oct 18, 2006 at 11:36 UTC

    The simplest approach is probably the clearest.

    if ( $item1 eq $item3 or $item2 eq $item3 ) { ... }

    Alternatively, there's always Quantum::Superpositions.

    use Quantum::Superpositions; if ($item3 eq any($item1, $item2)) { ... }
    --
    <http://dave.org.uk>

    "The first rule of Perl club is you do not talk about Perl club."
    -- Chip Salzenberg

Re: testing more than one item for equality
by Hofmator (Curate) on Oct 18, 2006 at 11:40 UTC
    No, there is no more direct way to achieve that, afaik. You could get rid of the $result variable like this
    if (grep { $_ eq $item3 } ($item1, $item2)) { #do something }
    or of course write
    if ($item1 eq $item3 or $item2 eq $item3) { ... }

    -- 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: testing more than one item for equality
by fenLisesi (Priest) on Oct 18, 2006 at 11:43 UTC
    I am assuming that the usual
    if ($item1 eq $item3 or $item2 eq $item3) { ## do something }
    is not good enough for you. Then you may get some help from CPAN:
Re: testing more than one item for equality
by Velaki (Chaplain) on Oct 18, 2006 at 11:50 UTC

    Perhaps something like this would help, since it handles an arbitrarily large list of elements, all of which must be equal to your target value for the statement to return true.

    #!/usr/bin/perl use strict; use warnings; my $item1 = 'foo'; my $item2 = 'foo'; my @list = ($item1, $item2); my $item3 = 'foo'; if ( scalar (grep { $_ eq $item3 } @list) == scalar @list) { print "All are equal.\n"; }

    Another way might be to synthesize a large string of $itemN = $item3 type code, and then eval it.

    Hope this helped,
    -v.

    Update:entire post rendered obsolete by blazar's excellent observation. I should've remembered DeMorgan's Law.

    "Perl. There is no substitute."
      if ( scalar (grep { $_ eq $item3 } @list) == scalar @list) { print "All are equal.\n"; }

      But that's simply not what he asked for, and even if it were those scalars are redundant, thus making your code bloated without adding to readability, since == imposes scalar context anyway. Thus

      if (grep { $_ eq $item3 } @list == @list) { print "All are equal.\n"; }

      Furthermore, you store into the @list array for the sole purpose of that comparison, but that's not necessary either: indeed checking for all the elements of a list to do something is the same thing as checking none of them not to do that particular thing, hence

      if (grep $_ neq $item3, $item1, $item2) { print "All are equal.\n"; }

        You're right. I forgot my DeMorgan's Law.

        Mea culpa,
        -v.
        "Perl. There is no substitute."
Re: testing more than one item for equality
by Melly (Hermit) on Oct 18, 2006 at 11:58 UTC

    Well, you could do it via a regex...

    if($item3 =~ /^(\Q$item1\E|\Q$item2\E)$/)

    <update>Tnx Hue-Bond for the \Q heads-up...</update>

    Tom Melly, tom@tomandlu.co.uk
Re: testing more than one item for equality
by blazar (Canon) on Oct 18, 2006 at 12:10 UTC
    I'm trying to do the following:
    if ( ($item1 or $item2) eq ($item3) ) { #do something; }

    Just use a junction. Oh no, Perl 5 doesn't have junctions. Well, then there's Quantum::Superpositions, but personally I wouldn't use it...

    In fact I've had to workaround:
    my $result = grep { $_ eq $item3 } ($item1, $item2); if ($result) { #do something }

    That's fine enough, has the advantage of having a straightforward generalization to more items and can be cast into a IMHO simpler form like thus:

    if (grep $_ eq $item3, $item1, $item2) { #do something }
    Is there a more direct statement one can use?

    More direct?

    if ($item1 eq $item3 or $item2 eq $item3) { #do something }

    This has the advantage of or's short circuiting, but in this case it really seems like a micro-optimization not worth the effort. To generalize to an arbitrary number of items you'd better need a hyperoperator that's not in Perl 5 either.

Re: testing more than one item for equality
by davido (Archbishop) on Oct 18, 2006 at 15:52 UTC

    I actually kind of like that grep solution because it allows you to implement both "this or that", or "this and that". Here's how:

    if( grep { $_ eq $test } ( $item1, $item2 ) ) { #..... This was an "or" } if( grep { $_ eq $test } ( $item1, $item2 ) == 2 ) { #..... This was an "and" }

    This works because grep in scalar context returns the number of times it matched. If it matched an equal number of times to the number of elements being tested, all elements matched, so you've got a "this and that and that and that" condition.

    This type of test is simplified even more (and definitely made easier to read) by using List::MoreUtils. That module offers an any and an all function:

    use List::MoreUtils qw/ any all /; #Here is an "or" condition. if( any { $_ == 1 } ( 1, 2, 3 ) ) { print "True.\n"; # This one is true because one of the elements matched. } #Here is an "and" condition. if( all { $_ == 1 } ( 1, 2, 3 ) ) { print "True.\n"; # This one is NOT true, because only one of the three elements mat +ched. }

    The List::MoreUtils solution is sort of like the grep solution, but IMHO, easier to "at a glance" understand what's going on, and thus a better choice for its clarity.

    Quantum::Superpositions could be used like this:

    use Quantum::Superpositions; if( any( 1, 2, 3 ) == 1 ) { print "True.\n"; } if( all( 1, 2, 3 ) == 1 ) { print "True.\n"; # This one fails because only one, not all elements matched. }

    Though the Quantum::Superpositions solution looked the simplest, the module's own author (TheDamian) states that it is not intended for real world use. It was written more as a proof of concept, so while it's fun to play with, you probably shouldn't put it in production code. My own experience is that it's stable, but often slow, probably because it's such a brute force generalized approach to problems that may have more efficient specific solutions.


    Dave

Log In?
Username:
Password:

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

How do I use this? | Other CB clients
Other Users?
Others rifling through the Monastery: (6)
As of 2014-12-28 18:40 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    Is guessing a good strategy for surviving in the IT business?





    Results (182 votes), past polls