Beefy Boxes and Bandwidth Generously Provided by pair Networks
"be consistent"
 
PerlMonks  

without looping, check if $var is found in @list

by Anonymous Monk
on Dec 14, 2006 at 01:51 UTC ( [id://589713]=perlquestion: print w/replies, xml ) Need Help??

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

Weird title I know, lol, but I couldn't think of a better way to put it. I know this has been done because I've seen it a few times but I have no idea what to search for.

Without looping over the @list, how can I check to see if $var is found in the list?

Ie:

my @list = qw(frog turtle tadpole); my $var = "turtle"; if ($var =~ m/@list/) {}; # not even close

Replies are listed 'Best First'.
Re: without looping, check if $var is found in @list
by davido (Cardinal) on Dec 14, 2006 at 01:56 UTC

    A loop cannot be avoided, but it can be implicit instead of coded longhand:

    my @list = qw/ frog turtle tadpole /; my $find = "turtle"; if( grep { $_ eq $find } @list ) { print "Found $find.\n"; }

    This isn't going to be the fastest way to do it, because it iterates through the entire list, even if it finds $find as the first element. For randomly distributed data, a smarter algorithm would quit as soon as the first instance is found, like this:

    my $found_flag = 0; foreach my $item ( @list ) { if( $item eq $find ) { $found_flag = 1; last; } } if( $found_flag ) { print "Found $find.\n"; }

    Either way, a loop is occurring. With grep, the loop happens behind the scenes. The second method spells it out explicitly, and tells it to exit as soon as the item you're looking for is found. That (on average) ought to be faster.

    You could also use List::Util's first() subroutine, like this:

    use List::Util; my @list = qw/ this that other /; my $find = "that"; if( first { $_ eq $find } @list ) { print "I found it!\n"; }

    I'm not sure how first() is implemented, but it should be implemented such that it exists as soon as it finds the match.


    Dave

      I swear there was a smaller solution than this, but I'll use this since it works and does exactly what I was looking for.

      I understand one way or another a loop must be done but I wasn't comfortable using foreach() for my current use of this snippet.

      Thanks!

      After experimenting with the code I seem to have weird results.

      I am implementing your code into an $sth->fetch. My results SORT OF work. The thing that's odd is if the very first fetch'd row does not match, NOTHING matches.

      However, if I remove any @choice but the first element, all the others work as expected. @choice is populated from a forum field where the user selects a few options via checkboxes.

      while ($sth->fetch) { if( grep { $_ eq $name } @choices ) {
      This behavoir is a little weird and I can't imagine what could be going wrong. Any ideas?
        It really is not clear what you are trying to do (and your extra reply doesn't help much either). Your description ("if the very first row does not match, NOTHING matches") is what I would expect, based on the snippet -- the data coming from the fetch would appear to have no bearing at all on whether your "@choices" array contains a match for the "$name" param.

        Based on the snippet you've presented, it looks like the same condition (with the same data, and therefore producing the same result) is being repeated on every iteration over the "while ()" loop.

        BTW, please consider signing up as a regular PerlMonks user (rather than Anonyous Monk), so you can get notifications of replies to your posts, and can update your posts when you want to (instead of posting more replies). It's harmless.

        I don't think I was clear enough..

        When my DB query prints out it's always in a specific order. If the option in @choices doesn't match the very first row retrieved from my database, everything fails. However, if the first row DOES match something in @choices all the other ones that match the critera work and the ones that don't are discarded as I expect.

        It's just that the first row is being strange, if it doesn't match @choices nothing will.

Re: without looping, check if $var is found in @list
by duckyd (Hermit) on Dec 14, 2006 at 02:51 UTC
    If you need to do this sort of operation (is $foo in @list) often, you should probably be using a hash rather than a list, even if your values are undef.
    #!/usr/local/bin/perl use strict; use warnings; my @list = qw(frog turtle tadpole); my $var = "turtle"; my %check_list; @check_list{ @list } = (); if( exists $check_list{ $var } ){ print "found $var!\n"; }
    Obviously you could get rid of @list entirely above.
Re: without looping, check if $var is found in @list
by imp (Priest) on Dec 14, 2006 at 02:11 UTC
    There are a variety of ways to check for membership in a list, but the one that is right for your situation depends on the problem you are trying to solve.

    For common approaches see the faq.

    Update
    Perl 6 will provide the any junction, which you can play with using the Perl6::Junction module for now.

    use Perl6::Junction qw/ any /; if (any(@list) eq $var) { }
Re: without looping, check if $var is found in @list
by leocharre (Priest) on Dec 14, 2006 at 04:31 UTC

    It should be pointed out your example is, i believe; horrendously wrong. In:

    if ($var=~ m/@list/ ){ print 'ok'; }

    What you are doing is attempting to match the @list inside of $var, and we know that $var does not hold all those things.

    This would have worked:

    #!/usr/bin/perl -w use strict; use warnings; my @list = qw(frog turtle tadpole); my $var = "turtle"; if ("@list" =~ /\b$var\b/) { print 'yup'; } # using quotes, we coerce the array to act as a string else { print 'nope'; }

    We didn't have to place the \b for this example to appear as if it worked. The \b means word boundary. Otherwise, we would match on 'turtles' and 'turtley'.

    the idea is : $lookinside =~ /$for/

      Using string interpolation on the array is a bad idea - it won't neccessarily get you what you want. For example:
      #!/usr/bin/perl -w use strict; use warnings; my @list = ('jumping turtle', 'frog', 'tadpole'); my $var = "turtle"; if ("@list" =~ /\b$var\b/) { print "yup\n"; }
      happily reports a match even though just "turtle" doesn't appear in the array, it's a "jumping turtle". I would be reluctant to assume that such behavior is desirable...
      • It will fail (false positive or false negative) if $var contains regexp characters.

        my $var = 'tad?pole'; my @list = ($var); print("@list" =~ /\b$var\b/ ? 1 : 0, "\n");
      • It will fail (false negative) if $var starts or ends with \W (because \b won't match).

        my $var = 'frog!'; my @list = ($var); print("@list" =~ /\b$var\b/ ? 1 : 0, "\n");
      • It will fail (false positive or false negative) if $var contains spaces. (Credit goes to duckyd.)

        my $var = 'turtle'; my @list = ("jumping $var"); print("@list" =~ /\b$var\b/ ? 1 : 0, "\n");

      If you knew a specific character will not be present in @list, you could use that character as a seperator.

      my $sep = "\x01"; my @list = ('jumping turtle', 'frog!', 'tad?pole'); for my $var (@list) { if (join($sep, '', @list, '') =~ /\Q$sep$var$sep\E/) { print "Found $var\n" } else { print "Didn't find $var\n" } }

      You could even make it faster by using index.

      my $sep = "\x01"; my @list = ('jumping turtle', 'frog!', 'tad?pole'); for my $var (@list) { if (index(join($sep, '', @list, ''), "$sep$var$sep") >= 0) { print "Found $var\n" } else { print "Didn't find $var\n" } }

      But really, are either of those solutions anywhere near as readable as those already suggested? That's without even taking into account the limitation of disallowing $sep from being in @list.

      Update: A few small corrections: Removed extra parens, fixed third snippet.

Re: without looping, check if $var is found in @list
by sulfericacid (Deacon) on Dec 14, 2006 at 03:09 UTC
    I'd have to agree with duckyd on this one. A hash is probably the way to go on this one. This is my version..
    my @choices = qq(one two three four five); my %choices; $choices{$_}++ for @choices; while ($sth->fetch) { if (exists $choices{$var}) { # } }


    "Age is nothing more than an inaccurate number bestowed upon us at birth as just another means for others to judge and classify us"

    sulfericacid
Re: without looping, check if $var is found in @list
by Anonymous Monk on Dec 14, 2006 at 12:05 UTC
    without asking Monks, check if $you can attempt @homework

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others rifling through the Monastery: (4)
As of 2024-04-25 12:19 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found