Beefy Boxes and Bandwidth Generously Provided by pair Networks
Perl Monk, Perl Meditation
 
PerlMonks  

How to check if a variable's value is equal to a member of a list of values

by cls33 (Novice)
on Mar 25, 2013 at 13:04 UTC ( [id://1025292]=perlquestion: print w/replies, xml ) Need Help??

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

I have a program where I need to check to see if a variable is equal to any one of a list of 1000+ values. Since I'll need to check this several times over the course of the program's execution, I'm wonder what the most efficient way to do this is.

My first though is to read all 1000+ of these values into a hash on startup (there is no DB backing this) where the values I'm checking against are the keys and then just using something like 1 for the value. Then when I need to do a see if the variable is one of these values, just check the hash using the current value of the variable as the key and see if I get anything "true" back...

Sound like I'm on the right track? Or is there a "better" way?

Replies are listed 'Best First'.
Re: How to check if a variable's value is equal to a member of a list of values
by McA (Priest) on Mar 25, 2013 at 13:08 UTC

    Like so often: It depends.

    How long are the keys and how much + do you have? I think that your hash approach is good as far as you have enough memory to store it. And the more you have to query the more it is profitable to build this hash initially.

    McA

      And if the hash happens to grow too big, you can tie it to an optimized cached on disk hash using DB_File, BerkeleyDB or similar module.

      One thing though, it's not necessary to store the 1 in the values. Keep them empty and use exists()

      my %set; @set{@values} = (); ... if (exists $set{$key}) ...

      Update: Fixed a syntax error (incorrect type of parens). Thanks go to choroba.

      Jenda
      Enoch was right!
      Enjoy the last years of Rome.

        Seems like I'm on the right track, but this is a more elegant implementation. I was not previously familiar with the ability to suck an entire array into a hash using the @hash{@array}=() syntax.

        One quick syntactical question... using this method, how would I set each hash key to a defined static value? Using something like:

        @hash{@array}='MyValue';

        Only seems to set the value for the first key...
      I don't see the OP mentioning whether the values to be checked are integer or floating point. Uncertainties in representing floating point values could make hash test useless.

        This is an interesting point. Do you know what value is taken for the hash key? The string representation or the floating point binary representation?

        When the first is true than what you said falls in the whole class of floating point related problems, e.g. equality of floats.

        McA

Re: How to check if a variable's value is equal to a member of a list of values
by CountOrlok (Friar) on Mar 25, 2013 at 13:16 UTC

    There are several ways. Fastest is probably to put the 1000+ values in a hash (as the keys, let their value be 1 if you don't care otherwise. Then you just need to check if exists($myhash{$variable})

    Another way available since version 5.10, I believe, is if your 1000+ values are in an array you can do if (@myarray ~~ $variable) { #do something }

      Hi,

      intersting: Because I'm curious: Is this substantially different (memory footprint, performance) than:

      if(grep { $_ eq $string } @array) {

      McA

        It should

      • smartmatch is implemented in C
      • grep doesn't stop after matching.

        Unfortunately all the use cases of smartmatch are hard to remember.

        For repeated lookups a prepared hash scales certainly better.

        You're free to check this with benchmarks or search for older discussions.¹

        I hoped smartmatch could at least handle the stringification limitation of hashes, but nope:

        DB<141> $h1={} => {} DB<142> $h1 ~~ [$h1] => "" DB<143> 5 ~~ [5] => 1

        Cheers Rolf

        ( addicted to the Perl Programming Language)

        Update

        ¹) Brian got deep into benchmarking :) http://stackoverflow.com/questions/3951812/how-fast-is-perls-smart-match-operator-for-searching-scalar-in-an-array

        Just to toss in a tidbit, and not trying to hijack the thread, but while benchmarking an alternative to using the smart match operator, I discovered that using map in scalar content performed better than grep.
        if( map { /$string/ } @array) {
        rather than...
        if( grep { /$string/ } @array) {

        I was surprised, because either way you'd be getting a non-undef value to satisfy the conditional, but map did it faster. I can post my code in a new thread if anyone's interested, otherwise, carry on! :)

      Be careful as on newer perl versions:

      $ perl -le '@a = (1,2,3); print "fred\n" if @a ~~ 1;' # prints nothing $ perl -le '@a = (1,2,3); print "fred\n" if 1 ~~ @a;' fred

      I think it changed at 5.10.1 but I didn't check that.

Re: How to check if a variable's value is equal to a member of a list of values
by LanX (Saint) on Mar 25, 2013 at 14:09 UTC
    could you please elaborate more, I don't really understand your usage of values and keys.

    lookups of strings (!) are most efficient with hashes, ...

    > then just using something like 1 for the value.

    ... and you don't need to assign any value, undef is enough

    my %look; @look{@set_of_values}=(); ... print "gotcha" if exists $look{$to_check};

    You're free to put anything other than undef in the slots if you need to do further checks.

    Someone proposed using smartmatch ~~, but I doubt this scales well in repeated use (if their is no magic background optimization happening)

    Cheers Rolf

    ( addicted to the Perl Programming Language)

Re: How to check if a variable's value is equal to a member of a list of values
by punch_card_don (Curate) on Mar 25, 2013 at 14:14 UTC
    If your values are members of ranges, you could go through the list once at startup and remember just the start/ends of the ranges, then compare to those. For example, if your list is like:

    1, 2, 3, 4, 5, 453, 454, 455, 456, 457, 458

    You'd remember only 1 - 5 and 453 - 458, then check if x is within any stored ranges.




    Time flies like an arrow. Fruit flies like a banana.
Re: How to check if a variable's value is equal to a member of a list of values
by sundialsvc4 (Abbot) on Mar 25, 2013 at 15:06 UTC

    Most commonly, I stuff the values as hash-members with a do-nothing value of '1' for each entry, just as you suggested in the OP.   But I have also iterated through a list e.g. with the grep() function and don’t often see much difference ... because my applications are not starving for microseconds (like, e.g. the high-performance applications frequently written by BrowserUK in which this governs the whole design) and the entire list is of such a size that it all fits in real-memory anyhow.   You probably have the luxury of choice in this case, where your decision does not seem to be “forced” by this one consideration alone.

    Probably, I would let my design be guided by what else you need to do with this list of numbers, and with the data that is associated with them.   Is it, literally, just a list of numbers, such that you only have to see if the number exists and such that, having done so, there is absolutely nothing else associated with that number?   Questions like that.

Log In?
Username:
Password:

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

How do I use this?Last hourOther CB clients
Other Users?
Others admiring the Monastery: (6)
As of 2024-03-19 08:38 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found