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

I have this hash
my %hash_keys = ( a => 00, b => 01, c => 02, d => 03, e => 04, f => 05, g => 06 );
I want to randomly select a key from %hash_keys such that its value is between 02 and 05, for instance. That is, I want to randomly between keys c, d, e, and f. What would be the easist way of doing this?

Replies are listed 'Best First'.
Re: How to randomly select a key whose value is between a range?
by golux (Chaplain) on Sep 04, 2015 at 22:49 UTC
    Construct the list of keys which have values within your desired range. Pull a random key out of that list, and you're done!
    #!/usr/bin/perl # Libraries use strict; use warnings; # User-defined my $min = 2; my $max = 5; my %hash = ( a => 00, b => 01, c => 02, d => 03, e => 04, f => 05, g => 06 ); # Main program my @match = grep { $hash{$_} >= $min and $hash{$_} <= $max } keys %has +h; my $rand = $match[rand @match]; printf "Random key = '%s', Value = '%s'\n", $rand, $hash{$rand};
    say  substr+lc crypt(qw $i3 SI$),4,5
Re: How to randomly select a key whose value is between a range?
by davido (Cardinal) on Sep 05, 2015 at 05:18 UTC

    How many hash elements will you be working with? How dense is the distribution of values? Are all values between some range existent? How often does the hash's set of elements change? How much of the hash's total range will your target range span? Are the values actually integers, or will they be floating point?

    If I were trying to come up with a solution that I would be happy with for my own purposes, I would want to know answers to these questions first. There are a lot of generalized solutions that could work, but to get a solution that is best for your specific needs, it would be useful if we knew a few more specifics.


    Dave

Re: How to randomly select a key whose value is between a range?
by stevieb (Canon) on Sep 05, 2015 at 00:59 UTC

    Hash keys are never guaranteed to be in order, so selecting based on a number will not work to get you your middle keys.

    If your keys are really all single letters, this will work (note that you'll have to either stringify the values or do hex work on them if they are indeed hex):

    #!/usr/bin/perl use warnings; use strict; my %hash = ( a => 00, b => 01, c => 02, d => 03, e => 04, f => 05, g => 06 ); my @keys = ('c'..'f'); print $hash{$keys[int(rand(@keys))]} . "\n";

    However, I doubt this is your real info. Can you supply us with real data if this isn't it? If your hash isn't exactly what you've shown here, the answers you are provided with are unlikely to work with a hash with keys that are not a single letter, or values that are not sequential.

      ... and notice how golux neatly side-stepped that issue in his (excellent) post #1:   the relevant range of keys was extracted (using grep as a clever alternative to a loop, to iterate through the entire set of key returned by keys), and placed into an array, which of course uses a zero-based numeric index.   The keys were selected from this array.   As long as you determine that the list contains at least one key, this approach always works.

      If you need to randomly select more than one key within the range, such that the same key will never be selected twice, a good way to do so is:   select the array of keys (as shown), then shuffle the array, then draw from the top.   Array::Shuffle and Algorithm::Numerical::Shuffle both illustrate how to do this.   (Fair warning:   read the fine-print in delete with regard to arrays, if you’re considering other ways to do this.)