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

Hi, In the code below, I'm trying to get a random element from a hash of arrays, given some_number.
1 my %hoa = ( 2 10 => [ "fred", "barney" ], 3 20 => [ "george", "jane", "elroy" ], 4 30 => [ "homer", "marge", "bart" ], 5 ); 6 my $ary = $hoa{$some_number}; 7 return $ary->[rand @$ary];
I've a feeling line 6 is redundant. Can get pick the random value without first storing the array reference in line 6 i.e. can lines 6 and 7 be done in a single step? Thanks for reading.

Replies are listed 'Best First'.
Re: Get random element from HoA
by FunkyMonk (Bishop) on Dec 27, 2007 at 12:54 UTC
    return $hoa{$some_number}->[rand @{ $hoa{$some_number} }];

    But i'm not sure that it's an improvement

      Thanks, FunkyMonk :) That's what I was looking for.
Re: Get random element from HoA
by Fletch (Bishop) on Dec 27, 2007 at 14:00 UTC

    If you wanted a random element from a random array (which I think is what you're aiming at):

    return do { my $arry = $hoa{ (keys %hoa)[rand %hoa] }; $arr->[ rand @$ +arry ] };

    Or possibly:

    return $hoa{ my $key = (keys %hoa)[ rand %hoa ] }->[ rand @{$hoa{$key} +} ];

    I can't think of a clean way to do it without some temporary or building a parallel data structure (containing either the indexes or a copy of all the elements).

    The cake is a lie.
    The cake is a lie.
    The cake is a lie.

      Wow, is that a "my" declaration in the hash key? That's new to me, thanks :)
Re: Get random element from HoA
by KurtSchwind (Chaplain) on Dec 27, 2007 at 13:07 UTC

    Maybe it's me, but your question is a bit confusing. Are you saying that $some_number would be the hash key that you want to get a random element from? As in, given $some_number = 20 you want to randomly get either "george" or "jane" or "elroy"?

    You can certainly index into your HoA without line 6.

    my $foo = $hoa{20}[1]; # assigns "jane" to $foo

    --
    I used to drive a Heisenbergmobile, but every time I looked at the speedometer, I got lost.
      Yes, it's some kind of score that's used for the key.
Re: Get random element from HoA
by Prof Vince (Friar) on Dec 28, 2007 at 13:52 UTC
    Be aware that Fletch's solution doesn't give you an uniform distribution over the array elements if all the arrays don't have the same length. If you prefer that, you can use this :

    my $count = do { # or keep track of it yourself while adding your elem +ents to %hoa use List::Util qw/sum/; sum map ~~@$_, values %hoa; }; sub randelt { my $i = int rand $count; my $v; while ((undef, $v) = each %hoa) { last if $i < @$v; $i -= @$v; } keys %hoa; # reset hash iterator $v->[$i]; };