Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to shuffle the values in a hash?

Tags:

shuffle

perl

I have a hash of string IDs. What is the best way to shuffle the IDs?

As an example, my hash assigns the following IDs:

this => 0
is => 1
a => 2 
test => 3

Now I'd like to randomly shuffle that. An example outcome would be:

this => 1
is => 0
a => 3
test => 2
like image 918
Frank Avatar asked Jul 13 '11 19:07

Frank


2 Answers

You could use the shuffle method in List::Util to help out:

use List::Util qw(shuffle);

...

my @values = shuffle(values %hash);
map { $hash{$_} = shift(@values) } (keys %hash);
like image 176
Mat Avatar answered Sep 23 '22 02:09

Mat


A hash slice would be the clearest way to me:

#!/usr/bin/perl

use strict;
use warnings;

use List::Util qw/shuffle/;
use Data::Dumper;

my %h = (
    this => 0,
    is   => 1,
    a    => 2,
    test => 3,
);

@h{keys %h} = shuffle values %h;

print Dumper \%h;

This has a drawback in that huge hashes would take up a lot of memory as you pull all of their keys and values out. A more efficient (from a memory standpoint) solution would be:

#!/usr/bin/perl

use strict;
use warnings;

use List::Util qw/shuffle/;
use Data::Dumper;

my %h = (
    this => 0,
    is   => 1,
    a    => 2,
    test => 3,
);

{ #bareblock to cause @keys to be garbage collected
    my @keys = shuffle keys %h;

    while (my $k1 = each %h) {
        my $k2 = shift @keys;
        @h{$k1, $k2} = @h{$k2, $k1};
    }
}

print Dumper \%h;

This code has the benefit of only having to duplicate the keys (rather than the keys and values).

The following code doesn't randomize the values (except on Perl 5.8.1 where the order of keys is guaranteed to be random), but it does mix up the order. It does have the benefit of working in place without too much extra memory usage:

#!/usr/bin/perl

use strict;
use warnings;

use List::Util qw/shuffle/;
use Data::Dumper;

my %h = (
    this => 0,
    is   => 1,
    a    => 2,
    test => 3,
);

my $k1  = each %h;
while (defined(my $k2 = each %h)) {
    @h{$k1, $k2} = @h{$k2, $k1};
    last unless defined($k1 = each %h);
}

print Dumper \%h;
like image 40
Chas. Owens Avatar answered Sep 19 '22 02:09

Chas. Owens