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

Hi Monks

I'm having dificulty with hashes. I'm trying to write a script that will take two lists and return strings of random combinations of their elements ie A1 {foo1, foo2, foo3, foo4...} and A2 {bar1, bar2, bar3, bar4...} to give:

  • foo1-bar3
  • foo2-bar1
  • foo1-bar4
  • and so on

    I need the strings to be unique so I decided to put them into a hash setting the string as the key. The code I've written works to a point. I get a hash of unique strings, but not always to the number I specify, ie $size=100, keys(%ligands) = 993. I'm guessing this is becuase some of the values were non-unique.

    Is there a way I can use $size to set the size of the hash and keep adding strings till I get to the limit?

    I've attached my current code for your ammusement. I'm quite new to programming and Perl so please be gentle, it my not be the most elegant ever written!

    Thanks for you help

    Hassan

    The code

    #!/usr/bin/perl # Test code for hash use Data::Random qw(:all); use List::Util qw(shuffle); # Read in two files containing Rgroups into 2 arrays unless ( open (A1_FILE, "A1.txt")) { print "Cannot find A1 list\n\n"; exit } unless ( open (A2_FILE, "A2.txt")) { print "Cannot find A2 list \n\n"; exit } my @A1 = <A1_FILE>; chomp (@A1); close (A1_FILE); my @A2 = <A2_FILE>; chomp (@A2); close (A2_FILE); my @A1_shuffled = shuffle (@A1); my @A2_shuffled = shuffle (@A2); print "\nEnter the name of the output file(give full path): "; my $outfile = <STDIN>; chomp $outfile; open (OUTPUT, ">$outfile"); print "\nEnter the number of ligands needed: "; my $size = <STDIN>; chomp $size; print "\nEnter the scaffold name: "; my $scaffold = <STDIN>; chomp $scaffold; my %ligands = (); for ($i=0; $i<$size; $i++) { my @random_A1 = rand_set( set => \@A1_shuffled, size => 1 ); my @random_A2 = rand_set( set => \@A2_shuffled, size => 1); $ligands{$ligand}=$i; } my $keys = keys(%ligands); my @temp = keys(%ligands); for ($z=0; $z<$keys; $z++){ print OUTPUT @temp[$z]; print OUTPUT "\n"; } close OUTPUT; print "\n"; print "\nSize of hash: ". keys(%ligands) . ".\n"; exit

    Replies are listed 'Best First'.
    Re: Filling hashes
    by davorg (Chancellor) on May 10, 2006 at 15:31 UTC

      I might be missing something, but you seem to be overcomplicating somewhat.

      (This is simplified for testing purposes)

      #!/usr/bin/perl use strict; use warnings; my @A = 'A' .. 'Z'; my @B = 1 .. 100; my $size = 10; my %strings; while (keys %strings < $size) { $strings{ $A[rand @A] . '-' . $B[rand @B] } = 1; } print join ',', keys %strings;
      --
      <http://dave.org.uk>

      "The first rule of Perl club is you do not talk about Perl club."
      -- Chip Salzenberg

    Re: Filling hashes
    by japhy (Canon) on May 10, 2006 at 15:28 UTC
      You haven't defined $ligand anywhere (if you had warnings and strict turned on, perl would've complained about that), yet you use it as the hash key. I'm not sure how your hash ends up with more than a single pair in it!

      I would rewrite the meat of your code as follows:

      # @key_set and @val_set are my names for @A1 and @A2 # there is no need to shuffle them! my %pairs; my $size = 100; # from the user until (keys(%pairs) == $size) { my $key = $key_set[rand @key_set]; my $val = $val_set[rand @val_set]; $pairs{"$key-$val"} = 1; # or even just: # $pairs{$key_set[rand @key_set] . "-" . $val_set[rand @val_set]} = +1; } for (keys %pairs) { print "$_\n"; }
      This seems to fulfill your goal.

      Jeff japhy Pinyan, P.L., P.M., P.O.D, X.S.: Perl, regex, and perl hacker
      How can we ever be the sold short or the cheated, we who for every service have long ago been overpaid? ~~ Meister Eckhart
    Re: Filling hashes
    by Hue-Bond (Priest) on May 10, 2006 at 15:28 UTC

      I think I'm not getting it well, is this what you want?

      use List::Util 'shuffle'; my @a1 = qw/one two three four five/; my @a2 = qw/1 2 3 4 5/; my @sa1 = shuffle @a1; my @sa2 = shuffle @a2; my @rnd_str = map { "$sa1[$_]-$sa2[$_]" } 0 ..$#sa1; ## show result print "$_\n" for @rnd_str; __END__ three-3 four-1 five-5 one-2 two-4

      --
      David Serrano

    Re: Filling hashes
    by leocharre (Priest) on May 10, 2006 at 17:35 UTC

      This may be of interest here.. This is a sub for generating a random string.

      sub random_string { my $length_of_randomstring=shift;# the length of # the random string to generate my @chars=('a'..'z','A'..'Z','0'..'9','_'); my $random_string; foreach (1..$length_of_randomstring) { # rand @chars will generate a random # number between 0 and scalar @chars $random_string.=$chars[rand @chars]; } return $random_string; }

      Usage is randmom_string(12);, you pass it the length you want the return string to be.